Index: head/sys/alpha/alpha/machdep.c =================================================================== --- head/sys/alpha/alpha/machdep.c (revision 130343) +++ head/sys/alpha/alpha/machdep.c (revision 130344) @@ -1,2410 +1,2410 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center and by Chris G. Demetriou. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_ddb.h" #include "opt_kstack_pages.h" #include "opt_msgbuf.h" #include "opt_maxmem.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include u_int64_t cycles_per_usec; u_int32_t cycles_per_sec; int cold = 1; struct platform platform; alpha_chipset_t chipset; struct bootinfo_kernel bootinfo; struct mtx icu_lock; struct user *proc0uarea; vm_offset_t proc0kstack; char machine[] = "alpha"; SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD, machine, 0, ""); static char cpu_model[128]; SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD, cpu_model, 0, ""); #ifdef DDB /* start and end of kernel symbol table */ void *ksym_start, *ksym_end; db_regs_t ddb_regs; #endif int alpha_unaligned_print = 1; /* warn about unaligned accesses */ int alpha_unaligned_fix = 1; /* fix up unaligned accesses */ int alpha_unaligned_sigbus = 0; /* don't SIGBUS on fixed-up accesses */ SYSCTL_INT(_machdep, CPU_UNALIGNED_PRINT, unaligned_print, CTLFLAG_RW, &alpha_unaligned_print, 0, ""); SYSCTL_INT(_machdep, CPU_UNALIGNED_FIX, unaligned_fix, CTLFLAG_RW, &alpha_unaligned_fix, 0, ""); SYSCTL_INT(_machdep, CPU_UNALIGNED_SIGBUS, unaligned_sigbus, CTLFLAG_RW, &alpha_unaligned_sigbus, 0, ""); static void cpu_startup(void *); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) struct msgbuf *msgbufp=0; long Maxmem = 0; long totalphysmem; /* total amount of physical memory in system */ long resvmem; /* amount of memory reserved for PROM */ long unusedmem; /* amount of memory for OS that we don't use */ long unknownmem; /* amount of memory with an unknown use */ int ncpus; /* number of cpus */ int promcons_dly_mkdev = 1; /* need to delay call to make_dev() */ void promcons_delayed_makedev(void); vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) #ifdef COMPAT_43 void osendsig(sig_t catcher, int sig, sigset_t *mask, u_long code); #endif #ifdef COMPAT_FREEBSD4 static void freebsd4_sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code); #endif static void get_fpcontext(struct thread *td, mcontext_t *mcp); static void identifycpu(void); static int set_fpcontext(struct thread *td, const mcontext_t *mcp); struct kva_md_info kmi; /* * Hooked into the shutdown chain; if the system is to be halted, * unconditionally drop back to the SRM console. */ static void alpha_srm_shutdown(void *junk, int howto) { if (howto & RB_HALT) { cpu_halt(); } } static void cpu_startup(dummy) void *dummy; { /* * Good {morning,afternoon,evening,night}. */ identifycpu(); /* startrtclock(); */ #ifdef PERFMON perfmon_init(); #endif printf("real memory = %ld (%ld MB)\n", alpha_ptob(Maxmem), alpha_ptob(Maxmem) / 1048576); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { int size1 = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08lx - 0x%08lx, %d bytes (%d pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE); } } vm_ksubmap_init(&kmi); printf("avail memory = %ld (%ld MB)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1048576); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); EVENTHANDLER_REGISTER(shutdown_final, alpha_srm_shutdown, 0, SHUTDOWN_PRI_LAST); } /* * Retrieve the platform name from the DSR. */ const char * alpha_dsr_sysname() { struct dsrdb *dsr; const char *sysname; /* * DSR does not exist on early HWRPB versions. */ if (hwrpb->rpb_version < HWRPB_DSRDB_MINVERS) return (NULL); dsr = (struct dsrdb *)(((caddr_t)hwrpb) + hwrpb->rpb_dsrdb_off); sysname = (const char *)((caddr_t)dsr + (dsr->dsr_sysname_off + sizeof(u_int64_t))); return (sysname); } /* * Lookup the system specified system variation in the provided table, * returning the model string on match. */ const char * alpha_variation_name(u_int64_t variation, const struct alpha_variation_table *avtp) { int i; for (i = 0; avtp[i].avt_model != NULL; i++) if (avtp[i].avt_variation == variation) return (avtp[i].avt_model); return (NULL); } /* * Generate a default platform name based for unknown system variations. */ const char * alpha_unknown_sysname() { static char s[128]; /* safe size */ snprintf(s, sizeof(s), "%s family, unknown model variation 0x%lx", platform.family, hwrpb->rpb_variation & SV_ST_MASK); return ((const char *)s); } static void identifycpu(void) { u_int64_t type, major, minor; u_int64_t amask; struct pcs *pcsp; char *cpuname[] = { "unknown", /* 0 */ "EV3", /* 1 */ "EV4 (21064)", /* 2 */ "Simulation", /* 3 */ "LCA Family", /* 4 */ "EV5 (21164)", /* 5 */ "EV45 (21064A)", /* 6 */ "EV56 (21164A)", /* 7 */ "EV6 (21264)", /* 8 */ "PCA56 (21164PC)", /* 9 */ "PCA57 (21164PC)", /* 10 */ "EV67 (21264A)", /* 11 */ "EV68CB (21264C)" /* 12 */ "EV68AL (21264B)", /* 13 */ "EV68CX (21264D)" /* 14 */ }; /* * print out CPU identification information. */ printf("%s\n%s, %ldMHz\n", platform.family, platform.model, hwrpb->rpb_cc_freq / 1000000); /* XXX true for 21164? */ printf("%ld byte page size, %d processor%s.\n", hwrpb->rpb_page_size, ncpus, ncpus == 1 ? "" : "s"); #if 0 /* this isn't defined for any systems that we run on? */ printf("serial number 0x%lx 0x%lx\n", ((long *)hwrpb->rpb_ssn)[0], ((long *)hwrpb->rpb_ssn)[1]); /* and these aren't particularly useful! */ printf("variation: 0x%lx, revision 0x%lx\n", hwrpb->rpb_variation, *(long *)hwrpb->rpb_revision); #endif pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id); /* cpu type */ type = pcsp->pcs_proc_type; major = (type & PCS_PROC_MAJOR) >> PCS_PROC_MAJORSHIFT; minor = (type & PCS_PROC_MINOR) >> PCS_PROC_MINORSHIFT; if (major < sizeof(cpuname)/sizeof(char *)) printf("CPU: %s major=%lu minor=%lu", cpuname[major], major, minor); else printf("CPU: major=%lu minor=%lu\n", major, minor); /* amask */ if (major >= PCS_PROC_EV56) { amask = 0xffffffff; /* 32 bit for printf */ amask = (~alpha_amask(amask)) & amask; printf(" extensions=0x%b\n", (u_int32_t) amask, "\020" "\001BWX" "\002FIX" "\003CIX" "\011MVI" "\012PRECISE" ); } else printf("\n"); /* PAL code */ printf("OSF PAL rev: 0x%lx\n", pcsp->pcs_palrevisions[PALvar_OSF1]); } extern char kernel_text[], _end[]; void alpha_init(pfn, ptb, bim, bip, biv) u_long pfn; /* first free PFN number */ u_long ptb; /* PFN of current level 1 page table */ u_long bim; /* bootinfo magic */ u_long bip; /* bootinfo pointer */ u_long biv; /* bootinfo version */ { int phys_avail_cnt; char *bootinfo_msg, *bootinfo_booted_kernel; vm_offset_t kernstart, kernend; vm_offset_t kernstartpfn, kernendpfn, pfn0, pfn1; struct mddt *mddtp; struct mddt_cluster *memc; int i, mddtweird; int cputype; char *p; /* NO OUTPUT ALLOWED UNTIL FURTHER NOTICE */ /* * Turn off interrupts (not mchecks) and floating point. * Make sure the instruction and data streams are consistent. */ (void)alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH); /* alpha_pal_wrfen(0); */ ALPHA_TBIA(); alpha_pal_imb(); /* * Get critical system information (if possible, from the * information provided by the boot program). */ bootinfo_msg = NULL; bootinfo_booted_kernel = NULL; if (bim == BOOTINFO_MAGIC) { if (biv == 0) { /* backward compat */ biv = *(u_long *)bip; bip += 8; } switch (biv) { case 1: { struct bootinfo_v1 *v1p = (struct bootinfo_v1 *)bip; bootinfo.ssym = v1p->ssym; bootinfo.esym = v1p->esym; bootinfo.kernend = v1p->kernend; bootinfo.modptr = v1p->modptr; bootinfo.envp = v1p->envp; /* hwrpb may not be provided by boot block in v1 */ if (v1p->hwrpb != NULL) { bootinfo.hwrpb_phys = ((struct rpb *)v1p->hwrpb)->rpb_phys; bootinfo.hwrpb_size = v1p->hwrpbsize; } else { bootinfo.hwrpb_phys = ((struct rpb *)HWRPB_ADDR)->rpb_phys; bootinfo.hwrpb_size = ((struct rpb *)HWRPB_ADDR)->rpb_size; } bcopy(v1p->boot_flags, bootinfo.boot_flags, min(sizeof v1p->boot_flags, sizeof bootinfo.boot_flags)); bcopy(v1p->booted_kernel, bootinfo.booted_kernel, min(sizeof v1p->booted_kernel, sizeof bootinfo.booted_kernel)); bootinfo_booted_kernel = bootinfo.booted_kernel; /* booted dev not provided in bootinfo */ init_prom_interface((struct rpb *) ALPHA_PHYS_TO_K0SEG(bootinfo.hwrpb_phys)); prom_getenv(PROM_E_BOOTED_DEV, bootinfo.booted_dev, sizeof bootinfo.booted_dev); break; } default: bootinfo_msg = "unknown bootinfo version"; goto nobootinfo; } } else { bootinfo_msg = "boot program did not pass bootinfo"; nobootinfo: bootinfo.ssym = (u_long)&_end; bootinfo.esym = (u_long)&_end; bootinfo.hwrpb_phys = ((struct rpb *)HWRPB_ADDR)->rpb_phys; bootinfo.hwrpb_size = ((struct rpb *)HWRPB_ADDR)->rpb_size; init_prom_interface((struct rpb *)HWRPB_ADDR); prom_getenv(PROM_E_BOOTED_OSFLAGS, bootinfo.boot_flags, sizeof bootinfo.boot_flags); prom_getenv(PROM_E_BOOTED_FILE, bootinfo.booted_kernel, sizeof bootinfo.booted_kernel); prom_getenv(PROM_E_BOOTED_DEV, bootinfo.booted_dev, sizeof bootinfo.booted_dev); } /* * Initialize the kernel's mapping of the RPB. It's needed for * lots of things. */ hwrpb = (struct rpb *)ALPHA_PHYS_TO_K0SEG(bootinfo.hwrpb_phys); /* * Remember how many cycles there are per microsecond, * so that we can use delay(). Round up, for safety. */ cycles_per_usec = (hwrpb->rpb_cc_freq + 999999) / 1000000; /* * Remember how many cycles per closk for coping with missed * clock interrupts. */ cycles_per_sec = hwrpb->rpb_cc_freq; /* Get the loader(8) metadata */ preload_metadata = (caddr_t)bootinfo.modptr; if (envmode == 1) kern_envp = static_env; else kern_envp = bootinfo.envp; /* Do basic tuning, hz etc */ init_param1(); /* * Initalize the (temporary) bootstrap console interface, so * we can use printf until the VM system starts being setup. * The real console is initialized before then. */ init_bootstrap_console(); /* OUTPUT NOW ALLOWED */ /* delayed from above */ if (bootinfo_msg) printf("WARNING: %s (0x%lx, 0x%lx, 0x%lx)\n", bootinfo_msg, bim, bip, biv); /* * Point interrupt/exception vectors to our own. */ alpha_pal_wrent(XentInt, ALPHA_KENTRY_INT); alpha_pal_wrent(XentArith, ALPHA_KENTRY_ARITH); alpha_pal_wrent(XentMM, ALPHA_KENTRY_MM); alpha_pal_wrent(XentIF, ALPHA_KENTRY_IF); alpha_pal_wrent(XentUna, ALPHA_KENTRY_UNA); alpha_pal_wrent(XentSys, ALPHA_KENTRY_SYS); /* * Clear pending machine checks and error reports, and enable * system- and processor-correctable error reporting. */ alpha_pal_wrmces(alpha_pal_rdmces() & ~(ALPHA_MCES_DSC|ALPHA_MCES_DPC)); /* Clear userland thread pointer */ alpha_pal_wrunique(0); /* * Find out what hardware we're on, and do basic initialization. */ cputype = hwrpb->rpb_type; if (cputype < 0) { /* * At least some white-box (NT) systems have SRM which * reports a systype that's the negative of their * blue-box (UNIX/OVMS) counterpart. */ cputype = -cputype; } if (cputype >= API_ST_BASE) { if (cputype >= napi_cpuinit + API_ST_BASE) { platform_not_supported(cputype); /* NOTREACHED */ } cputype -= API_ST_BASE; api_cpuinit[cputype].init(cputype); } else { if (cputype >= ncpuinit) { platform_not_supported(cputype); /* NOTREACHED */ } cpuinit[cputype].init(cputype); } snprintf(cpu_model, sizeof(cpu_model), "%s", platform.model); /* NO MORE FIRMWARE ACCESS ALLOWED */ #ifdef _PMAP_MAY_USE_PROM_CONSOLE /* * XXX (unless _PMAP_MAY_USE_PROM_CONSOLE is defined and * XXX pmap_uses_prom_console() evaluates to non-zero.) */ #endif /* * find out this system's page size */ if (hwrpb->rpb_page_size != PAGE_SIZE) panic("page size %ld != 8192?!", hwrpb->rpb_page_size); /* * Find the beginning and end of the kernel (and leave a * bit of space before the beginning for the bootstrap * stack). */ kernstart = trunc_page(kernel_text) - 2 * PAGE_SIZE; #ifdef DDB ksym_start = (void *)bootinfo.ssym; ksym_end = (void *)bootinfo.esym; kernend = (vm_offset_t)round_page(ksym_end); #else kernend = (vm_offset_t)round_page(_end); #endif /* But if the bootstrap tells us otherwise, believe it! */ if (bootinfo.kernend) kernend = round_page(bootinfo.kernend); if (preload_metadata == NULL) printf("WARNING: loader(8) metadata is missing!\n"); kernstartpfn = atop(ALPHA_K0SEG_TO_PHYS(kernstart)); kernendpfn = atop(ALPHA_K0SEG_TO_PHYS(kernend)); /* * Find out how much memory is available, by looking at * the memory cluster descriptors. This also tries to do * its best to detect things things that have never been seen * before... */ mddtp = (struct mddt *)(((caddr_t)hwrpb) + hwrpb->rpb_memdat_off); /* MDDT SANITY CHECKING */ mddtweird = 0; if (mddtp->mddt_cluster_cnt < 2) { mddtweird = 1; printf("WARNING: weird number of mem clusters: %ld\n", mddtp->mddt_cluster_cnt); } #ifdef DEBUG_CLUSTER printf("Memory cluster count: %d\n", mddtp->mddt_cluster_cnt); #endif phys_avail_cnt = 0; for (i = 0; i < mddtp->mddt_cluster_cnt; i++) { memc = &mddtp->mddt_clusters[i]; #ifdef DEBUG_CLUSTER printf("MEMC %d: pfn 0x%lx cnt 0x%lx usage 0x%lx\n", i, memc->mddt_pfn, memc->mddt_pg_cnt, memc->mddt_usage); #endif totalphysmem += memc->mddt_pg_cnt; if (memc->mddt_usage & MDDT_mbz) { mddtweird = 1; printf("WARNING: mem cluster %d has weird " "usage 0x%lx\n", i, memc->mddt_usage); unknownmem += memc->mddt_pg_cnt; continue; } if (memc->mddt_usage & MDDT_NONVOLATILE) { /* XXX should handle these... */ printf("WARNING: skipping non-volatile mem " "cluster %d\n", i); unusedmem += memc->mddt_pg_cnt; continue; } if (memc->mddt_usage & MDDT_PALCODE) { resvmem += memc->mddt_pg_cnt; continue; } /* * We have a memory cluster available for system * software use. We must determine if this cluster * holds the kernel. */ /* * XXX If the kernel uses the PROM console, we only use the * XXX memory after the kernel in the first system segment, * XXX to avoid clobbering prom mapping, data, etc. */ physmem += memc->mddt_pg_cnt; pfn0 = memc->mddt_pfn; pfn1 = memc->mddt_pfn + memc->mddt_pg_cnt; if (pfn0 <= kernendpfn && kernstartpfn <= pfn1) { /* * Must compute the location of the kernel * within the segment. */ #ifdef DEBUG_CLUSTER printf("Cluster %d contains kernel\n", i); #endif if (!pmap_uses_prom_console()) { if (pfn0 < kernstartpfn) { /* * There is a chunk before the kernel. */ #ifdef DEBUG_CLUSTER printf("Loading chunk before kernel: " "0x%lx / 0x%lx\n", pfn0, kernstartpfn); #endif phys_avail[phys_avail_cnt] = alpha_ptob(pfn0); phys_avail[phys_avail_cnt+1] = alpha_ptob(kernstartpfn); phys_avail_cnt += 2; } } if (kernendpfn < pfn1) { /* * There is a chunk after the kernel. */ #ifdef DEBUG_CLUSTER printf("Loading chunk after kernel: " "0x%lx / 0x%lx\n", kernendpfn, pfn1); #endif phys_avail[phys_avail_cnt] = alpha_ptob(kernendpfn); phys_avail[phys_avail_cnt+1] = alpha_ptob(pfn1); phys_avail_cnt += 2; } } else { /* * Just load this cluster as one chunk. */ #ifdef DEBUG_CLUSTER printf("Loading cluster %d: 0x%lx / 0x%lx\n", i, pfn0, pfn1); #endif phys_avail[phys_avail_cnt] = alpha_ptob(pfn0); phys_avail[phys_avail_cnt+1] = alpha_ptob(pfn1); phys_avail_cnt += 2; } } phys_avail[phys_avail_cnt] = 0; /* * Dump out the MDDT if it looks odd... */ if (mddtweird) { printf("\n"); printf("complete memory cluster information:\n"); for (i = 0; i < mddtp->mddt_cluster_cnt; i++) { printf("mddt %d:\n", i); printf("\tpfn %lx\n", mddtp->mddt_clusters[i].mddt_pfn); printf("\tcnt %lx\n", mddtp->mddt_clusters[i].mddt_pg_cnt); printf("\ttest %lx\n", mddtp->mddt_clusters[i].mddt_pg_test); printf("\tbva %lx\n", mddtp->mddt_clusters[i].mddt_v_bitaddr); printf("\tbpa %lx\n", mddtp->mddt_clusters[i].mddt_p_bitaddr); printf("\tbcksum %lx\n", mddtp->mddt_clusters[i].mddt_bit_cksum); printf("\tusage %lx\n", mddtp->mddt_clusters[i].mddt_usage); } printf("\n"); } Maxmem = physmem; #ifdef MAXMEM /* * MAXMEM define is in kilobytes. */ Maxmem = alpha_btop(MAXMEM * 1024); #endif /* * hw.physmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ if ((p = getenv("hw.physmem")) != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(p, &ep, 0); if ((ep != p) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", p); else Maxmem = alpha_btop(AllowMem); freeenv(p); } while (physmem > Maxmem) { int i = phys_avail_cnt - 2; size_t sz = alpha_btop(phys_avail[i+1] - phys_avail[i]); size_t nsz; if (physmem - sz > Maxmem) { phys_avail[i] = 0; phys_avail[i+1] = 0; phys_avail_cnt -= 2; physmem -= sz; } else { nsz = sz - (physmem - Maxmem); phys_avail[i+1] = phys_avail[i] + alpha_ptob(nsz); physmem -= (sz - nsz); } } init_param2(physmem); /* * Initialize error message buffer (at end of core). */ { size_t sz = round_page(MSGBUF_SIZE); int i = phys_avail_cnt - 2; /* shrink so that it'll fit in the last segment */ if (phys_avail[i+1] - phys_avail[i] < sz) sz = phys_avail[i+1] - phys_avail[i]; phys_avail[i+1] -= sz; msgbufp = (struct msgbuf*) ALPHA_PHYS_TO_K0SEG(phys_avail[i+1]); msgbufinit(msgbufp, sz); /* Remove the last segment if it now has no pages. */ if (phys_avail[i] == phys_avail[i+1]) { phys_avail[i] = 0; phys_avail[i+1] = 0; } /* warn if the message buffer had to be shrunk */ if (sz != round_page(MSGBUF_SIZE)) printf("WARNING: %ld bytes not available for msgbuf in last cluster (%ld used)\n", round_page(MSGBUF_SIZE), sz); } proc_linkup(&proc0, &ksegrp0, &kse0, &thread0); /* * Init mapping for u page(s) for proc 0 */ proc0uarea = (struct user *)pmap_steal_memory(UAREA_PAGES * PAGE_SIZE); proc0kstack = pmap_steal_memory(KSTACK_PAGES * PAGE_SIZE); proc0.p_uarea = proc0uarea; thread0.td_kstack = proc0kstack; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; /* * Setup the per-CPU data for the bootstrap cpu. */ { /* This is not a 'struct user' */ size_t sz = round_page(KSTACK_PAGES * PAGE_SIZE); pcpup = (struct pcpu *) pmap_steal_memory(sz); pcpu_init(pcpup, alpha_pal_whami(), sz); alpha_pal_wrval((u_int64_t) pcpup); PCPU_GET(next_asn) = 1; /* 0 used for proc0 pmap */ PCPU_SET(curthread, &thread0); #ifdef SMP thread0.td_md.md_kernnest = 1; #endif } /* * Initalize the real console, so the the bootstrap console is * no longer necessary. Note this now involves mutexes as part * of some operations so needs to be after proc0/thread0/curthread * become valid. */ #ifndef NO_SIO if (platform.cons_init) { platform.cons_init(); promcndetach(); } #else if (platform.cons_init) platform.cons_init(); promcndetach(); cninit(); #endif /* * Check to see if promcons needs to make_dev() now, * doing it before now crashes with kernel stack issues. */ if (promcons_dly_mkdev > 1) promcons_delayed_makedev(); promcons_dly_mkdev = 0; /* * Initialize the virtual memory system, and set the * page table base register in proc 0's PCB. */ pmap_bootstrap(ALPHA_PHYS_TO_K0SEG(alpha_ptob(ptb)), hwrpb->rpb_max_asn); hwrpb->rpb_vptb = VPTBASE; hwrpb->rpb_checksum = hwrpb_checksum(); /* * Initialize the rest of proc 0's PCB, and cache its physical * address. */ thread0.td_md.md_pcbpaddr = (struct pcb *)ALPHA_K0SEG_TO_PHYS((vm_offset_t)thread0.td_pcb); /* * Set the kernel sp, reserving space for an (empty) trapframe, * and make proc0's trapframe pointer point to it for sanity. */ thread0.td_frame = (struct trapframe *)thread0.td_pcb - 1; thread0.td_pcb->pcb_hw.apcb_ksp = (u_int64_t)thread0.td_frame; mutex_init(); mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_RECURSE); mtx_init(&icu_lock, "icu", NULL, MTX_SPIN); /* * Look at arguments passed to us and compute boothowto. */ #ifdef KADB boothowto |= RB_KDB; #endif /* boothowto |= RB_KDB | RB_GDB; */ for (p = bootinfo.boot_flags; p && *p != '\0'; p++) { /* * Note that we'd really like to differentiate case here, * but the Alpha AXP Architecture Reference Manual * says that we shouldn't. */ switch (*p) { case 'a': /* autoboot */ case 'A': boothowto &= ~RB_SINGLE; break; #ifdef DEBUG case 'c': /* crash dump immediately after autoconfig */ case 'C': boothowto |= RB_DUMP; break; #endif #if defined(DDB) case 'd': /* break into the kernel debugger ASAP */ case 'D': boothowto |= RB_KDB; break; case 'g': /* use kernel gdb */ case 'G': boothowto |= RB_GDB; break; #endif case 'h': /* always halt, never reboot */ case 'H': boothowto |= RB_HALT; break; #if 0 case 'm': /* mini root present in memory */ case 'M': boothowto |= RB_MINIROOT; break; #endif case 'n': /* askname */ case 'N': boothowto |= RB_ASKNAME; break; case 's': /* single-user (default, supported for sanity) */ case 'S': boothowto |= RB_SINGLE; break; case 'v': case 'V': boothowto |= RB_VERBOSE; bootverbose = 1; break; default: printf("Unrecognized boot flag '%c'.\n", *p); break; } } /* * Catch case of boot_verbose set in environment. */ if ((p = getenv("boot_verbose")) != NULL) { if (strcmp(p, "yes") == 0 || strcmp(p, "YES") == 0) { boothowto |= RB_VERBOSE; bootverbose = 1; } freeenv(p); } /* * Pick up kernelname. */ if (bootinfo_booted_kernel) { strncpy(kernelname, bootinfo_booted_kernel, min(sizeof(kernelname), sizeof bootinfo.booted_kernel) - 1); } else if ((p = getenv("kernelname")) != NULL) { strncpy(kernelname, p, sizeof(kernelname) - 1); freeenv(p); } /* * Initialize debuggers, and break into them if appropriate. */ #ifdef DDB kdb_init(); if (boothowto & RB_KDB) { printf("Boot flags requested debugger\n"); breakpoint(); } #endif /* * Figure out the number of cpus in the box, from RPB fields. * Really. We mean it. */ for (i = 0; i < hwrpb->rpb_pcs_cnt; i++) { struct pcs *pcsp; pcsp = (struct pcs *)((char *)hwrpb + hwrpb->rpb_pcs_off + (i * hwrpb->rpb_pcs_size)); if ((pcsp->pcs_flags & PCS_PP) != 0) ncpus++; } /* * Figure out our clock frequency, from RPB fields. */ hz = hwrpb->rpb_intr_freq >> 12; if (!(60 <= hz && hz <= 10240)) { hz = 1024; #ifdef DIAGNOSTIC printf("WARNING: unbelievable rpb_intr_freq: %ld (%d hz)\n", hwrpb->rpb_intr_freq, hz); #endif } hwrpb_restart_setup(); alpha_pal_wrfen(0); } void bzero(void *buf, size_t len) { caddr_t p = buf; while (((vm_offset_t) p & (sizeof(u_long) - 1)) && len) { *p++ = 0; len--; } while (len >= sizeof(u_long) * 8) { *(u_long*) p = 0; *((u_long*) p + 1) = 0; *((u_long*) p + 2) = 0; *((u_long*) p + 3) = 0; len -= sizeof(u_long) * 8; *((u_long*) p + 4) = 0; *((u_long*) p + 5) = 0; *((u_long*) p + 6) = 0; *((u_long*) p + 7) = 0; p += sizeof(u_long) * 8; } while (len >= sizeof(u_long)) { *(u_long*) p = 0; len -= sizeof(u_long); p += sizeof(u_long); } while (len) { *p++ = 0; len--; } } void DELAY(int n) { unsigned long pcc0, pcc1, curcycle, cycles; int usec; if (n == 0) return; pcc0 = alpha_rpcc() & 0xffffffffUL; cycles = 0; usec = 0; while (usec <= n) { /* * Get the next CPU cycle count. The assumption here * is that we can't have wrapped twice past 32 bits worth * of CPU cycles since we last checked. */ pcc1 = alpha_rpcc() & 0xffffffffUL; if (pcc1 < pcc0) { curcycle = (pcc1 + 0x100000000UL) - pcc0; } else { curcycle = pcc1 - pcc0; } /* * We now have the number of processor cycles since we * last checked. Add the current cycle count to the * running total. If it's over cycles_per_usec, increment * the usec counter. */ cycles += curcycle; while (cycles > cycles_per_usec) { usec++; cycles -= cycles_per_usec; } pcc0 = pcc1; } } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top 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. */ #ifdef COMPAT_43 void osendsig(sig_t catcher, int sig, sigset_t *mask, u_long code) { struct proc *p; struct thread *td; osiginfo_t *sip, ksi; struct trapframe *frame; struct sigacts *psp; int oonstack, fsize, rndfsize; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); frame = td->td_frame; fsize = sizeof ksi; rndfsize = ((fsize + 15) / 16) * 16; oonstack = sigonstack(alpha_pal_rdusp()); /* * Allocate and validate space for the signal handler * context. Note that if the stack is in P0 space, the * call to grow() is a nop, and the useracc() check * will fail if the process has not already allocated * the space with a `brk'. */ if ((td->td_pflags & TDP_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sip = (osiginfo_t *)((caddr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size - rndfsize); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sip = (osiginfo_t *)(alpha_pal_rdusp() - rndfsize); mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * Build the signal context to be used by sigreturn. */ ksi.si_sc.sc_onstack = (oonstack) ? 1 : 0; SIG2OSIG(*mask, ksi.si_sc.sc_mask); ksi.si_sc.sc_pc = frame->tf_regs[FRAME_PC]; ksi.si_sc.sc_ps = frame->tf_regs[FRAME_PS]; /* copy the registers. */ fill_regs(td, (struct reg *)ksi.si_sc.sc_regs); ksi.si_sc.sc_regs[R_ZERO] = 0xACEDBADE; /* magic number */ ksi.si_sc.sc_regs[R_SP] = alpha_pal_rdusp(); /* save the floating-point state, if necessary, then copy it. */ alpha_fpstate_save(td, 1); /* XXX maybe write=0 */ ksi.si_sc.sc_ownedfp = td->td_md.md_flags & MDTD_FPUSED; bcopy(&td->td_pcb->pcb_fp, (struct fpreg *)ksi.si_sc.sc_fpregs, sizeof(struct fpreg)); ksi.si_sc.sc_fp_control = td->td_pcb->pcb_fp_control; bzero(ksi.si_sc.sc_reserved, sizeof ksi.si_sc.sc_reserved); /* XXX */ ksi.si_sc.sc_xxx1[0] = 0; /* XXX */ ksi.si_sc.sc_xxx1[1] = 0; /* XXX */ ksi.si_sc.sc_traparg_a0 = frame->tf_regs[FRAME_TRAPARG_A0]; ksi.si_sc.sc_traparg_a1 = frame->tf_regs[FRAME_TRAPARG_A1]; ksi.si_sc.sc_traparg_a2 = frame->tf_regs[FRAME_TRAPARG_A2]; ksi.si_sc.sc_xxx2[0] = 0; /* XXX */ ksi.si_sc.sc_xxx2[1] = 0; /* XXX */ ksi.si_sc.sc_xxx2[2] = 0; /* XXX */ /* Fill in POSIX parts */ ksi.si_signo = sig; ksi.si_code = code; ksi.si_value.sigval_ptr = NULL; /* XXX */ /* * copy the frame out to userland. */ if (copyout((caddr_t)&ksi, (caddr_t)sip, fsize) != 0) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); sigexit(td, SIGILL); return; } /* * Set up the registers to return to sigcode. */ frame->tf_regs[FRAME_PC] = PS_STRINGS - szosigcode; frame->tf_regs[FRAME_A0] = sig; frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */ PROC_LOCK(p); mtx_lock(&psp->ps_mtx); if (SIGISMEMBER(psp->ps_siginfo, sig)) frame->tf_regs[FRAME_A1] = (u_int64_t)sip; else frame->tf_regs[FRAME_A1] = code; frame->tf_regs[FRAME_A2] = (u_int64_t)&sip->si_sc; frame->tf_regs[FRAME_T12] = (u_int64_t)catcher; /* t12 is pv */ alpha_pal_wrusp((unsigned long)sip); } #endif #ifdef COMPAT_FREEBSD4 static void freebsd4_sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code) { struct proc *p; struct thread *td; struct trapframe *frame; struct sigacts *psp; struct sigframe4 sf, *sfp; int oonstack, rndfsize; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); frame = td->td_frame; oonstack = sigonstack(alpha_pal_rdusp()); rndfsize = ((sizeof(sf) + 15) / 16) * 16; /* save user context */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; fill_regs(td, (struct reg *)sf.sf_uc.uc_mcontext.mc_regs); sf.sf_uc.uc_mcontext.mc_regs[R_SP] = alpha_pal_rdusp(); sf.sf_uc.uc_mcontext.mc_regs[R_ZERO] = 0xACEDBADE; /* magic number */ sf.sf_uc.uc_mcontext.mc_regs[R_PS] = frame->tf_regs[FRAME_PS]; sf.sf_uc.uc_mcontext.mc_regs[R_PC] = frame->tf_regs[FRAME_PC]; sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A0] = frame->tf_regs[FRAME_TRAPARG_A0]; sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A1] = frame->tf_regs[FRAME_TRAPARG_A1]; sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A2] = frame->tf_regs[FRAME_TRAPARG_A2]; /* * Allocate and validate space for the signal handler * context. Note that if the stack is in P0 space, the * call to grow() is a nop, and the useracc() check * will fail if the process has not already allocated * the space with a `brk'. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe4 *)((caddr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size - rndfsize); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe4 *)(alpha_pal_rdusp() - rndfsize); mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* save the floating-point state, if necessary, then copy it. */ alpha_fpstate_save(td, 1); sf.sf_uc.uc_mcontext.mc_ownedfp = td->td_md.md_flags & MDTD_FPUSED; bcopy(&td->td_pcb->pcb_fp, (struct fpreg *)sf.sf_uc.uc_mcontext.mc_fpregs, sizeof(struct fpreg)); sf.sf_uc.uc_mcontext.mc_fp_control = td->td_pcb->pcb_fp_control; #ifdef COMPAT_OSF1 /* * XXX Create an OSF/1-style sigcontext and associated goo. */ #endif /* * copy the frame out to userland. */ if (copyout((caddr_t)&sf, (caddr_t)sfp, sizeof(sf)) != 0) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); sigexit(td, SIGILL); return; } /* * Set up the registers to return to sigcode. */ frame->tf_regs[FRAME_PC] = PS_STRINGS - szfreebsd4_sigcode; frame->tf_regs[FRAME_A0] = sig; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); if (SIGISMEMBER(psp->ps_siginfo, sig)) { frame->tf_regs[FRAME_A1] = (u_int64_t)&(sfp->sf_si); /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void*)frame->tf_regs[FRAME_TRAPARG_A0]; } else frame->tf_regs[FRAME_A1] = code; frame->tf_regs[FRAME_A2] = (u_int64_t)&(sfp->sf_uc); frame->tf_regs[FRAME_T12] = (u_int64_t)catcher; /* t12 is pv */ frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */ alpha_pal_wrusp((unsigned long)sfp); } #endif /* COMPAT_FREEBSD4 */ void sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code) { struct proc *p; struct thread *td; struct trapframe *frame; struct sigacts *psp; struct sigframe sf, *sfp; int oonstack, rndfsize; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); #ifdef COMPAT_FREEBSD4 if (SIGISMEMBER(psp->ps_freebsd4, sig)) { freebsd4_sendsig(catcher, sig, mask, code); return; } #endif #ifdef COMPAT_43 if (SIGISMEMBER(psp->ps_osigset, sig)) { osendsig(catcher, sig, mask, code); return; } #endif frame = td->td_frame; oonstack = sigonstack(alpha_pal_rdusp()); rndfsize = ((sizeof(sf) + 15) / 16) * 16; /* save user context */ bzero(&sf, sizeof(struct sigframe)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; fill_regs(td, (struct reg *)sf.sf_uc.uc_mcontext.mc_regs); sf.sf_uc.uc_mcontext.mc_regs[R_SP] = alpha_pal_rdusp(); sf.sf_uc.uc_mcontext.mc_regs[R_ZERO] = 0xACEDBADE; /* magic number */ sf.sf_uc.uc_mcontext.mc_regs[R_PS] = frame->tf_regs[FRAME_PS]; sf.sf_uc.uc_mcontext.mc_regs[R_PC] = frame->tf_regs[FRAME_PC]; sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A0] = frame->tf_regs[FRAME_TRAPARG_A0]; sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A1] = frame->tf_regs[FRAME_TRAPARG_A1]; sf.sf_uc.uc_mcontext.mc_regs[R_TRAPARG_A2] = frame->tf_regs[FRAME_TRAPARG_A2]; sf.sf_uc.uc_mcontext.mc_format = _MC_REV0_SIGFRAME; /* * Allocate and validate space for the signal handler * context. Note that if the stack is in P0 space, the * call to grow() is a nop, and the useracc() check * will fail if the process has not already allocated * the space with a `brk'. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe *)((caddr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size - rndfsize); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe *)(alpha_pal_rdusp() - rndfsize); mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* save the floating-point state, if necessary, then copy it. */ alpha_fpstate_save(td, 1); sf.sf_uc.uc_mcontext.mc_ownedfp = td->td_md.md_flags & MDTD_FPUSED; bcopy(&td->td_pcb->pcb_fp, (struct fpreg *)sf.sf_uc.uc_mcontext.mc_fpregs, sizeof(struct fpreg)); sf.sf_uc.uc_mcontext.mc_fp_control = td->td_pcb->pcb_fp_control; #ifdef COMPAT_OSF1 /* * XXX Create an OSF/1-style sigcontext and associated goo. */ #endif /* * copy the frame out to userland. */ if (copyout((caddr_t)&sf, (caddr_t)sfp, sizeof(sf)) != 0) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); sigexit(td, SIGILL); return; } /* * Set up the registers to return to sigcode. */ frame->tf_regs[FRAME_PC] = PS_STRINGS - szsigcode; frame->tf_regs[FRAME_A0] = sig; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); if (SIGISMEMBER(psp->ps_siginfo, sig)) { frame->tf_regs[FRAME_A1] = (u_int64_t)&(sfp->sf_si); /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void*)frame->tf_regs[FRAME_TRAPARG_A0]; } else frame->tf_regs[FRAME_A1] = code; frame->tf_regs[FRAME_A2] = (u_int64_t)&(sfp->sf_uc); frame->tf_regs[FRAME_T12] = (u_int64_t)catcher; /* t12 is pv */ frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */ alpha_pal_wrusp((unsigned long)sfp); } /* * Build siginfo_t for SA thread */ void cpu_thread_siginfo(int sig, u_long code, siginfo_t *si) { struct proc *p; struct thread *td; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); bzero(si, sizeof(*si)); si->si_signo = sig; si->si_code = code; /* XXXKSE fill other fields */ } /* * 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 * state to gain improper privileges. * * MPSAFE */ #ifdef COMPAT_43 int osigreturn(struct thread *td, struct osigreturn_args /* { struct osigcontext *sigcntxp; } */ *uap) { struct osigcontext *scp, ksc; struct proc *p = td->td_proc; scp = uap->sigcntxp; /* * Fetch the entire context structure at once for speed. */ if (copyin((caddr_t)scp, (caddr_t)&ksc, sizeof ksc)) return (EFAULT); /* * XXX - Should we do this. What if we get a "handcrafted" * but valid sigcontext that hasn't the magic number? */ if (ksc.sc_regs[R_ZERO] != 0xACEDBADE) /* magic number */ return (EINVAL); PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Restore the user-supplied information */ if (ksc.sc_onstack) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif /* * longjmp is still implemented by calling osigreturn. The new * sigmask is stored in sc_reserved, sc_mask is only used for * backward compatibility. */ SIGSETOLD(td->td_sigmask, ksc.sc_mask); SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); set_regs(td, (struct reg *)ksc.sc_regs); td->td_frame->tf_regs[FRAME_PC] = ksc.sc_pc; td->td_frame->tf_regs[FRAME_PS] = (ksc.sc_ps | ALPHA_PSL_USERSET) & ~ALPHA_PSL_USERCLR; td->td_frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */ alpha_pal_wrusp(ksc.sc_regs[R_SP]); /* XXX ksc.sc_ownedfp ? */ alpha_fpstate_drop(td); bcopy((struct fpreg *)ksc.sc_fpregs, &td->td_pcb->pcb_fp, sizeof(struct fpreg)); td->td_pcb->pcb_fp_control = ksc.sc_fp_control; return (EJUSTRETURN); } #endif /* COMPAT_43 */ #ifdef COMPAT_FREEBSD4 /* * MPSAFE */ int freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args /* { const struct ucontext4 *sigcntxp; } */ *uap) { struct ucontext4 uc; const struct ucontext4 *ucp; struct pcb *pcb; unsigned long val; struct proc *p; int error; ucp = uap->sigcntxp; pcb = td->td_pcb; p = td->td_proc; /* * Fetch the entire context structure at once for speed. * Note that struct osigcontext is smaller than a ucontext_t, * so even if copyin() faults, we may have actually gotten a complete * struct osigcontext. */ error = copyin(ucp, &uc, sizeof(ucontext_t)); if (error != 0) { #ifdef COMPAT_43 if (((struct osigcontext*)&uc)->sc_regs[R_ZERO] == 0xACEDBADE) return osigreturn(td, (struct osigreturn_args *)uap); #endif return (error); } #ifdef COMPAT_43 if (((struct osigcontext*)&uc)->sc_regs[R_ZERO] == 0xACEDBADE) return osigreturn(td, (struct osigreturn_args *)uap); #endif /* * Restore the user-supplied information */ set_regs(td, (struct reg *)uc.uc_mcontext.mc_regs); val = (uc.uc_mcontext.mc_regs[R_PS] | ALPHA_PSL_USERSET) & ~ALPHA_PSL_USERCLR; td->td_frame->tf_regs[FRAME_PS] = val; td->td_frame->tf_regs[FRAME_PC] = uc.uc_mcontext.mc_regs[R_PC]; td->td_frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */ alpha_pal_wrusp(uc.uc_mcontext.mc_regs[R_SP]); PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (uc.uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = uc.uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); /* XXX ksc.sc_ownedfp ? */ alpha_fpstate_drop(td); bcopy((struct fpreg *)uc.uc_mcontext.mc_fpregs, &td->td_pcb->pcb_fp, sizeof(struct fpreg)); td->td_pcb->pcb_fp_control = uc.uc_mcontext.mc_fp_control; return (EJUSTRETURN); } #endif /* COMPAT_FREEBSD4 */ /* * MPSAFE */ int sigreturn(struct thread *td, struct sigreturn_args /* { const struct __ucontext *sigcntxp; } */ *uap) { ucontext_t uc; const ucontext_t *ucp; struct pcb *pcb; unsigned long val; struct proc *p; int error; ucp = uap->sigcntxp; pcb = td->td_pcb; p = td->td_proc; /* * Fetch the entire context structure at once for speed. * Note that struct osigcontext is smaller than a ucontext_t, * so even if copyin() faults, we may have actually gotten a complete * struct osigcontext. * XXX we'll *still* be getting osigcontext's here due to longjmp(3) * brain damage. */ error = copyin(ucp, &uc, sizeof(ucontext_t)); if (error != 0) { #ifdef COMPAT_43 if (((struct osigcontext*)&uc)->sc_regs[R_ZERO] == 0xACEDBADE) return osigreturn(td, (struct osigreturn_args *)uap); #endif return (error); } #ifdef COMPAT_43 if (((struct osigcontext*)&uc)->sc_regs[R_ZERO] == 0xACEDBADE) return osigreturn(td, (struct osigreturn_args *)uap); #endif /* * Restore the user-supplied information */ if ((error = set_fpcontext(td, &uc.uc_mcontext)) != 0) return (error); set_regs(td, (struct reg *)uc.uc_mcontext.mc_regs); val = (uc.uc_mcontext.mc_regs[R_PS] | ALPHA_PSL_USERSET) & ~ALPHA_PSL_USERCLR; td->td_frame->tf_regs[FRAME_PS] = val; td->td_frame->tf_regs[FRAME_PC] = uc.uc_mcontext.mc_regs[R_PC]; td->td_frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */ alpha_pal_wrusp(uc.uc_mcontext.mc_regs[R_SP]); PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (uc.uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = uc.uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { prom_halt(1); } void cpu_idle(void) { /* Insert code to halt (until next interrupt) for the idle loop */ } /* * Clear registers on exec */ void exec_setregs(struct thread *td, u_long entry, u_long stack, u_long ps_strings) { struct trapframe *tfp = td->td_frame; bzero(tfp->tf_regs, FRAME_SIZE * sizeof tfp->tf_regs[0]); bzero(&td->td_pcb->pcb_fp, sizeof td->td_pcb->pcb_fp); td->td_pcb->pcb_fp_control = 0; td->td_pcb->pcb_fp.fpr_cr = (FPCR_DYN_NORMAL | FPCR_INVD | FPCR_DZED | FPCR_OVFD | FPCR_INED | FPCR_UNFD); alpha_pal_wrusp(stack); tfp->tf_regs[FRAME_PS] = ALPHA_PSL_USERSET; tfp->tf_regs[FRAME_PC] = entry & ~3; tfp->tf_regs[FRAME_A0] = stack; /* a0 = sp */ tfp->tf_regs[FRAME_A1] = 0; /* a1 = rtld cleanup */ tfp->tf_regs[FRAME_A2] = 0; /* a2 = rtld object */ tfp->tf_regs[FRAME_A3] = PS_STRINGS; /* a3 = ps_strings */ tfp->tf_regs[FRAME_T12] = tfp->tf_regs[FRAME_PC]; /* a.k.a. PV */ tfp->tf_regs[FRAME_FLAGS] = 0; /* full restore */ td->td_md.md_flags &= ~MDTD_FPUSED; alpha_fpstate_drop(td); } int ptrace_set_pc(struct thread *td, unsigned long addr) { struct trapframe *tp = td->td_frame; tp->tf_regs[FRAME_PC] = addr; return 0; } static int ptrace_read_int(struct thread *td, vm_offset_t addr, u_int32_t *v) { struct iovec iov; struct uio uio; iov.iov_base = (caddr_t) v; iov.iov_len = sizeof(u_int32_t); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = (off_t)addr; uio.uio_resid = sizeof(u_int32_t); uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = UIO_READ; uio.uio_td = td; return proc_rwmem(td->td_proc, &uio); } static int ptrace_write_int(struct thread *td, vm_offset_t addr, u_int32_t v) { struct iovec iov; struct uio uio; iov.iov_base = (caddr_t) &v; iov.iov_len = sizeof(u_int32_t); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = (off_t)addr; uio.uio_resid = sizeof(u_int32_t); uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = UIO_WRITE; uio.uio_td = td; return proc_rwmem(td->td_proc, &uio); } static u_int64_t ptrace_read_register(struct thread *td, int regno) { static int reg_to_frame[32] = { FRAME_V0, FRAME_T0, FRAME_T1, FRAME_T2, FRAME_T3, FRAME_T4, FRAME_T5, FRAME_T6, FRAME_T7, FRAME_S0, FRAME_S1, FRAME_S2, FRAME_S3, FRAME_S4, FRAME_S5, FRAME_S6, FRAME_A0, FRAME_A1, FRAME_A2, FRAME_A3, FRAME_A4, FRAME_A5, FRAME_T8, FRAME_T9, FRAME_T10, FRAME_T11, FRAME_RA, FRAME_T12, FRAME_AT, FRAME_GP, FRAME_SP, -1, /* zero */ }; if (regno == R_ZERO) return 0; return td->td_frame->tf_regs[reg_to_frame[regno]]; } static int ptrace_clear_bpt(struct thread *td, struct mdbpt *bpt) { return ptrace_write_int(td, bpt->addr, bpt->contents); } static int ptrace_set_bpt(struct thread *td, struct mdbpt *bpt) { int error; u_int32_t bpins = 0x00000080; error = ptrace_read_int(td, bpt->addr, &bpt->contents); if (error) return error; return ptrace_write_int(td, bpt->addr, bpins); } int ptrace_clear_single_step(struct thread *td) { if (td->td_md.md_flags & MDTD_STEP2) { ptrace_clear_bpt(td, &td->td_md.md_sstep[1]); ptrace_clear_bpt(td, &td->td_md.md_sstep[0]); td->td_md.md_flags &= ~MDTD_STEP2; } else if (td->td_md.md_flags & MDTD_STEP1) { ptrace_clear_bpt(td, &td->td_md.md_sstep[0]); td->td_md.md_flags &= ~MDTD_STEP1; } return 0; } int ptrace_single_step(struct thread *td) { int error; vm_offset_t pc = td->td_frame->tf_regs[FRAME_PC]; alpha_instruction ins; vm_offset_t addr[2]; /* places to set breakpoints */ int count = 0; /* count of breakpoints */ if (td->td_md.md_flags & (MDTD_STEP1|MDTD_STEP2)) panic("ptrace_single_step: step breakpoints not removed"); error = ptrace_read_int(td, pc, &ins.bits); if (error) return (error); switch (ins.branch_format.opcode) { case op_j: /* Jump: target is register value */ addr[0] = ptrace_read_register(td, ins.jump_format.rs) & ~3; count = 1; break; case op_br: case op_fbeq: case op_fblt: case op_fble: case op_bsr: case op_fbne: case op_fbge: case op_fbgt: case op_blbc: case op_beq: case op_blt: case op_ble: case op_blbs: case op_bne: case op_bge: case op_bgt: /* Branch: target is pc+4+4*displacement */ addr[0] = pc + 4; addr[1] = pc + 4 + 4 * ins.branch_format.displacement; count = 2; break; default: addr[0] = pc + 4; count = 1; } td->td_md.md_sstep[0].addr = addr[0]; error = ptrace_set_bpt(td, &td->td_md.md_sstep[0]); if (error) return (error); if (count == 2) { td->td_md.md_sstep[1].addr = addr[1]; error = ptrace_set_bpt(td, &td->td_md.md_sstep[1]); if (error) { ptrace_clear_bpt(td, &td->td_md.md_sstep[0]); return (error); } td->td_md.md_flags |= MDTD_STEP2; } else td->td_md.md_flags |= MDTD_STEP1; return (error); } int alpha_pa_access(vm_offset_t pa) { #if 0 int i; for (i = 0; phys_avail[i] != 0; i += 2) { if (pa < phys_avail[i]) continue; if (pa < phys_avail[i+1]) return VM_PROT_READ|VM_PROT_WRITE; } return 0; #else return VM_PROT_READ|VM_PROT_WRITE; #endif } int fill_regs(td, regs) struct thread *td; struct reg *regs; { struct pcb *pcb = td->td_pcb; struct trapframe *tp = td->td_frame; #define C(r) regs->r_regs[R_ ## r] = tp->tf_regs[FRAME_ ## r] C(V0); C(T0); C(T1); C(T2); C(T3); C(T4); C(T5); C(T6); C(T7); C(S0); C(S1); C(S2); C(S3); C(S4); C(S5); C(S6); C(A0); C(A1); C(A2); C(A3); C(A4); C(A5); C(T8); C(T9); C(T10); C(T11); C(RA); C(T12); C(AT); C(GP); #undef C regs->r_regs[R_ZERO] = tp->tf_regs[FRAME_PC]; regs->r_regs[R_SP] = pcb->pcb_hw.apcb_usp; return (0); } int set_regs(td, regs) struct thread *td; struct reg *regs; { struct pcb *pcb = td->td_pcb; struct trapframe *tp = td->td_frame; #define C(r) tp->tf_regs[FRAME_ ## r] = regs->r_regs[R_ ## r] C(V0); C(T0); C(T1); C(T2); C(T3); C(T4); C(T5); C(T6); C(T7); C(S0); C(S1); C(S2); C(S3); C(S4); C(S5); C(S6); C(A0); C(A1); C(A2); C(A3); C(A4); C(A5); C(T8); C(T9); C(T10); C(T11); C(RA); C(T12); C(AT); C(GP); #undef C tp->tf_regs[FRAME_PC] = regs->r_regs[R_ZERO]; pcb->pcb_hw.apcb_usp = regs->r_regs[R_SP]; return (0); } int get_mcontext(struct thread *td, mcontext_t *mcp, int flags) { /* * Use a trapframe for getsetcontext, so just copy the * threads trapframe. */ bcopy(td->td_frame, &mcp->mc_regs, sizeof(struct trapframe)); if (flags & GET_MC_CLEAR_RET) { mcp->mc_regs[FRAME_V0] = 0; mcp->mc_regs[FRAME_A4] = 0; mcp->mc_regs[FRAME_A3] = 0; } /* * When the thread is the current thread, the user stack pointer * is not in the PCB; it must be read from the PAL. */ if (td == curthread) { mcp->mc_regs[FRAME_SP] = alpha_pal_rdusp(); mcp->mc_thrptr = alpha_pal_rdunique(); } else mcp->mc_thrptr = 0; mcp->mc_format = _MC_REV0_TRAPFRAME; PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(alpha_pal_rdusp()) ? 1 : 0; PROC_UNLOCK(curthread->td_proc); get_fpcontext(td, mcp); return (0); } int set_mcontext(struct thread *td, const mcontext_t *mcp) { int ret; unsigned long val; if ((mcp->mc_format != _MC_REV0_TRAPFRAME) && (mcp->mc_format != _MC_REV0_SIGFRAME)) return (EINVAL); else if ((ret = set_fpcontext(td, mcp)) != 0) return (ret); /* * NOTE: We only need to restore mc_thrptr when the ucontext format * is _MC_REV0_TRAPFRAME. Only get_mcontext() above creates such * contexts and that's also the only place where we save the thread * pointer in the context. */ if (mcp->mc_format == _MC_REV0_SIGFRAME) { set_regs(td, (struct reg *)&mcp->mc_regs); val = (mcp->mc_regs[R_PS] | ALPHA_PSL_USERSET) & ~ALPHA_PSL_USERCLR; td->td_frame->tf_regs[FRAME_PS] = val; td->td_frame->tf_regs[FRAME_PC] = mcp->mc_regs[R_PC]; td->td_frame->tf_regs[FRAME_FLAGS] = 0; if (td == curthread) alpha_pal_wrusp(mcp->mc_regs[R_SP]); } else { if (td == curthread) { alpha_pal_wrusp(mcp->mc_regs[FRAME_SP]); alpha_pal_wrunique(mcp->mc_thrptr); } else { td->td_pcb->pcb_hw.apcb_usp = mcp->mc_regs[FRAME_SP]; td->td_pcb->pcb_hw.apcb_unique = mcp->mc_thrptr; } /* * The context is a trapframe, so just copy it over the * threads frame. */ bcopy(&mcp->mc_regs, td->td_frame, sizeof(struct trapframe)); } return (0); } static void get_fpcontext(struct thread *td, mcontext_t *mcp) { register_t s; s = intr_disable(); if ((td->td_md.md_flags & MDTD_FPUSED) == 0) { intr_restore(s); mcp->mc_ownedfp = _MC_FPOWNED_NONE; } else if (PCPU_GET(fpcurthread) == td) { /* See comments in alpha_fpstate_save() regarding FEN. */ if (td != curthread) alpha_pal_wrfen(1); /* * The last field (fpr_cr) of struct fpreg isn't * included in mc_fpregs, but it immediately follows * it in mcontext_t. */ savefpstate((struct fpreg *)&mcp->mc_fpregs); if (td != curthread) alpha_pal_wrfen(0); intr_restore(s); mcp->mc_ownedfp = _MC_FPOWNED_FPU; } else { /* * The thread doesn't own the FPU so get the state from * the PCB. */ intr_restore(s); bcopy(&td->td_pcb->pcb_fp, &mcp->mc_fpregs, sizeof(td->td_pcb->pcb_fp)); mcp->mc_ownedfp = _MC_FPOWNED_PCB; } /* There's no harm in always doing the following. */ mcp->mc_fp_control = td->td_pcb->pcb_fp_control; } static int set_fpcontext(struct thread *td, const mcontext_t *mcp) { register_t s; if (mcp->mc_ownedfp == _MC_FPOWNED_NONE) { /* XXX - Drop fpu state so we get a clean state? */ alpha_fpstate_drop(td); } else if ((mcp->mc_ownedfp != _MC_FPOWNED_FPU) && (mcp->mc_ownedfp != _MC_FPOWNED_PCB)) return (EINVAL); else { s = intr_disable(); if (PCPU_GET(fpcurthread) == td) { /* * The last field (fpr_cr) of struct fpreg isn't * included in mc_fpregs, but it immediately follows * it in mcontext_t. */ restorefpstate((struct fpreg *)&mcp->mc_fpregs); intr_restore(s); } else { /* Just save the state in the PCB. */ intr_restore(s); bcopy(&mcp->mc_fpregs, &td->td_pcb->pcb_fp, sizeof (td->td_pcb->pcb_fp)); } td->td_pcb->pcb_fp_control = mcp->mc_fp_control; } return (0); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { return (ENOSYS); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { return (ENOSYS); } int fill_fpregs(td, fpregs) struct thread *td; struct fpreg *fpregs; { alpha_fpstate_save(td, 0); bcopy(&td->td_pcb->pcb_fp, fpregs, sizeof *fpregs); return (0); } int set_fpregs(td, fpregs) struct thread *td; struct fpreg *fpregs; { alpha_fpstate_drop(td); bcopy(fpregs, &td->td_pcb->pcb_fp, sizeof *fpregs); return (0); } #ifndef DDB void Debugger(const char *msg) { printf("Debugger(\"%s\") called.\n", msg); } #endif /* no DDB */ static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); void alpha_fpstate_check(struct thread *td) { /* * For SMP, we should check the fpcurthread of each cpu. */ #ifndef SMP register_t s; s = intr_disable(); if (td->td_pcb->pcb_hw.apcb_flags & ALPHA_PCB_FLAGS_FEN) if (td != PCPU_GET(fpcurthread)) panic("alpha_check_fpcurthread: bogus"); intr_restore(s); #endif } #define SET_FEN(td) \ (td)->td_pcb->pcb_hw.apcb_flags |= ALPHA_PCB_FLAGS_FEN #define CLEAR_FEN(td) \ (td)->td_pcb->pcb_hw.apcb_flags &= ~ALPHA_PCB_FLAGS_FEN /* * Save the floating point state in the pcb. Use this to get read-only * access to the floating point state. If write is true, the current * fp process is cleared so that fp state can safely be modified. The * process will automatically reload the changed state by generating a * FEN trap. */ void alpha_fpstate_save(struct thread *td, int write) { register_t s; s = intr_disable(); if (td != NULL && td == PCPU_GET(fpcurthread)) { /* * If curthread != fpcurthread, then we need to enable FEN * so that we can dump the fp state. */ alpha_pal_wrfen(1); /* * Save the state in the pcb. */ savefpstate(&td->td_pcb->pcb_fp); if (write) { /* * If fpcurthread == curthread, just ask the * PALcode to disable FEN, otherwise we must * clear the FEN bit in fpcurthread's pcb. */ if (PCPU_GET(fpcurthread) == curthread) alpha_pal_wrfen(0); else CLEAR_FEN(PCPU_GET(fpcurthread)); PCPU_SET(fpcurthread, NULL); } else { /* * Make sure that we leave FEN enabled if * curthread == fpcurthread. We must have at most * one process with FEN enabled. Note that FEN * must already be set in fpcurthread's pcb. */ if (curthread != PCPU_GET(fpcurthread)) alpha_pal_wrfen(0); } } intr_restore(s); } /* * Relinquish ownership of the FP state. This is called instead of * alpha_save_fpstate() if the entire FP state is being changed * (e.g. on sigreturn). */ void alpha_fpstate_drop(struct thread *td) { register_t s; s = intr_disable(); if (td == PCPU_GET(fpcurthread)) { if (td == curthread) { /* * Disable FEN via the PALcode. This will * clear the bit in the pcb as well. */ alpha_pal_wrfen(0); } else { /* * Clear the FEN bit of the pcb. */ CLEAR_FEN(td); } PCPU_SET(fpcurthread, NULL); } intr_restore(s); } /* * Switch the current owner of the fp state to p, reloading the state * from the pcb. */ void alpha_fpstate_switch(struct thread *td) { register_t s; /* * Enable FEN so that we can access the fp registers. */ s = intr_disable(); alpha_pal_wrfen(1); if (PCPU_GET(fpcurthread)) { /* * Dump the old fp state if its valid. */ savefpstate(&PCPU_GET(fpcurthread)->td_pcb->pcb_fp); CLEAR_FEN(PCPU_GET(fpcurthread)); } /* * Remember the new FP owner and reload its state. */ PCPU_SET(fpcurthread, td); restorefpstate(&PCPU_GET(fpcurthread)->td_pcb->pcb_fp); /* * If the new owner is curthread, leave FEN enabled, otherwise * mark its PCB so that it gets FEN when we context switch to * it later. */ if (td != curthread) { alpha_pal_wrfen(0); SET_FEN(td); } td->td_md.md_flags |= MDTD_FPUSED; intr_restore(s); } /* * Initialise a struct pcpu. */ void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t sz) { pcpu->pc_idlepcbphys = vtophys((vm_offset_t) &pcpu->pc_idlepcb); pcpu->pc_idlepcb.apcb_ksp = (u_int64_t) ((caddr_t) pcpu + sz - sizeof(struct trapframe)); pcpu->pc_idlepcb.apcb_ptbr = thread0.td_pcb->pcb_hw.apcb_ptbr; pcpu->pc_current_asngen = 1; } Index: head/sys/amd64/amd64/machdep.c =================================================================== --- head/sys/amd64/amd64/machdep.c (revision 130343) +++ head/sys/amd64/amd64/machdep.c (revision 130344) @@ -1,1825 +1,1825 @@ /*- * Copyright (c) 2003 Peter Wemm. * 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. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 */ #include __FBSDID("$FreeBSD$"); #include "opt_atalk.h" #include "opt_atpic.h" #include "opt_compat.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_isa.h" #include "opt_kstack_pages.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_perfmon.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PERFMON #include #endif #include #ifdef SMP #include #endif #include #include #include #include #include /* Sanity check for __curthread() */ CTASSERT(offsetof(struct pcpu, pc_curthread) == 0); extern u_int64_t hammer_time(u_int64_t, u_int64_t); extern void dblfault_handler(void); extern void printcpuinfo(void); /* XXX header file */ extern void identify_cpu(void); extern void panicifcpuunsupported(void); #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) static void cpu_startup(void *); static void get_fpcontext(struct thread *td, mcontext_t *mcp); static int set_fpcontext(struct thread *td, const mcontext_t *mcp); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) int _udatasel, _ucodesel, _ucode32sel; int cold = 1; long Maxmem = 0; vm_paddr_t phys_avail[20]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) struct kva_md_info kmi; static struct trapframe proc0_tf; struct region_descriptor r_gdt, r_idt; struct pcpu __pcpu[MAXCPU]; struct mtx icu_lock; #ifdef DDB void *ksym_start, *ksym_end; #endif static void cpu_startup(dummy) void *dummy; { /* * Good {morning,afternoon,evening,night}. */ startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %ju (%ju MB)\n", ptoa((uintmax_t)Maxmem), ptoa((uintmax_t)Maxmem) / 1048576); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { vm_paddr_t size; size = phys_avail[indx + 1] - phys_avail[indx]; printf( "0x%016jx - 0x%016jx, %ju bytes (%ju pages)\n", (uintmax_t)phys_avail[indx], (uintmax_t)phys_avail[indx + 1] - 1, (uintmax_t)size, (uintmax_t)size / PAGE_SIZE); } } vm_ksubmap_init(&kmi); printf("avail memory = %ju (%ju MB)\n", ptoa((uintmax_t)cnt.v_free_count), ptoa((uintmax_t)cnt.v_free_count) / 1048576); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); cpu_setregs(); } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top 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; sigset_t *mask; u_long code; { struct sigframe sf, *sfp; struct proc *p; struct thread *td; struct sigacts *psp; char *sp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); regs = td->td_frame; oonstack = sigonstack(regs->tf_rsp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; bcopy(regs, &sf.sf_uc.uc_mcontext.mc_rdi, sizeof(*regs)); sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext); /* magic */ get_fpcontext(td, &sf.sf_uc.uc_mcontext); fpstate_drop(td); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sp = td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct sigframe); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sp = (char *)regs->tf_rsp - sizeof(struct sigframe) - 128; /* Align to 16 bytes. */ sfp = (struct sigframe *)((unsigned long)sp & ~0xFul); /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ regs->tf_rdi = sig; /* arg 1 in %rdi */ regs->tf_rdx = (register_t)&sfp->sf_uc; /* arg 3 in %rdx */ if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ regs->tf_rsi = (register_t)&sfp->sf_si; /* arg 2 in %rsi */ sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; regs->tf_rcx = regs->tf_addr; /* arg 4 in %rcx */ } else { /* Old FreeBSD-style arguments. */ regs->tf_rsi = code; /* arg 2 in %rsi */ regs->tf_rcx = regs->tf_addr; /* arg 4 in %rcx */ sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_rsp = (long)sfp; regs->tf_rip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_rflags &= ~PSL_T; regs->tf_cs = _ucodesel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } /* * Build siginfo_t for SA thread */ void cpu_thread_siginfo(int sig, u_long code, siginfo_t *si) { struct proc *p; struct thread *td; struct trapframe *regs; td = curthread; p = td->td_proc; regs = td->td_frame; PROC_LOCK_ASSERT(p, MA_OWNED); bzero(si, sizeof(*si)); si->si_signo = sig; si->si_code = code; si->si_addr = (void *)regs->tf_addr; /* XXXKSE fill other fields */ } /* * 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 * state to gain improper privileges. * * MPSAFE */ int sigreturn(td, uap) struct thread *td; struct sigreturn_args /* { const __ucontext *sigcntxp; } */ *uap; { ucontext_t uc; struct proc *p = td->td_proc; struct trapframe *regs; const ucontext_t *ucp; long rflags; int cs, error, ret; error = copyin(uap->sigcntxp, &uc, sizeof(uc)); if (error != 0) return (error); ucp = &uc; regs = td->td_frame; rflags = ucp->uc_mcontext.mc_rflags; /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_rflags for faults. Debuggers * should sometimes set it there too. tf_rflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(rflags & ~PSL_RF, regs->tf_rflags & ~PSL_RF)) { printf("sigreturn: rflags = 0x%lx\n", rflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("sigreturn: cs = 0x%x\n", cs); trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } ret = set_fpcontext(td, &ucp->uc_mcontext); if (ret != 0) return (ret); bcopy(&ucp->uc_mcontext.mc_rdi, regs, sizeof(*regs)); PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (ucp->uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = ucp->uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); td->td_pcb->pcb_flags |= PCB_FULLCTX; return (EJUSTRETURN); } #ifdef COMPAT_FREEBSD4 int freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args *uap) { return sigreturn(td, (struct sigreturn_args *)uap); } #endif /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Hook to idle the CPU when possible. In the SMP case we default to * off because a halted cpu will not currently pick up a new thread in the * run queue until the next timer tick. If turned on this will result in * approximately a 4.2% loss in real time performance in buildworld tests * (but improves user and sys times oddly enough), and saves approximately * 5% in power consumption on an idle machine (tests w/2xCPU 1.1GHz P3). * * XXX we need to have a cpu mask of idle cpus and generate an IPI or * otherwise generate some sort of interrupt to wake up cpus sitting in HLT. * Then we can have our cake and eat it too. * * XXX I'm turning it on for SMP as well by default for now. It seems to * help lock contention somewhat, and this is critical for HTT. -Peter */ static int cpu_idle_hlt = 1; SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW, &cpu_idle_hlt, 0, "Idle loop HLT enable"); static void cpu_idle_default(void) { /* * we must absolutely guarentee that hlt is the * absolute next instruction after sti or we * introduce a timing window. */ __asm __volatile("sti; hlt"); } /* * Note that we have to be careful here to avoid a race between checking * sched_runnable() and actually halting. If we don't do this, we may waste * the time between calling hlt and the next interrupt even though there * is a runnable process. */ void cpu_idle(void) { if (cpu_idle_hlt) { disable_intr(); if (sched_runnable()) enable_intr(); else (*cpu_idle_hook)(); } } /* Other subsystems (e.g., ACPI) can hook this later. */ void (*cpu_idle_hook)(void) = cpu_idle_default; /* * Clear registers on exec */ void exec_setregs(td, entry, stack, ps_strings) struct thread *td; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = td->td_frame; struct pcb *pcb = td->td_pcb; wrmsr(MSR_FSBASE, 0); wrmsr(MSR_KGSBASE, 0); /* User value while we're in the kernel */ pcb->pcb_fsbase = 0; pcb->pcb_gsbase = 0; load_ds(_udatasel); load_es(_udatasel); load_fs(_udatasel); load_gs(_udatasel); pcb->pcb_ds = _udatasel; pcb->pcb_es = _udatasel; pcb->pcb_fs = _udatasel; pcb->pcb_gs = _udatasel; bzero((char *)regs, sizeof(struct trapframe)); regs->tf_rip = entry; regs->tf_rsp = ((stack - 8) & ~0xFul) + 8; regs->tf_rdi = stack; /* argv */ regs->tf_rflags = PSL_USER | (regs->tf_rflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_cs = _ucodesel; /* * Reset the hardware debug registers if they were in use. * They won't have any meaning for the newly exec'd process. */ if (pcb->pcb_flags & PCB_DBREGS) { pcb->pcb_dr0 = 0; pcb->pcb_dr1 = 0; pcb->pcb_dr2 = 0; pcb->pcb_dr3 = 0; pcb->pcb_dr6 = 0; pcb->pcb_dr7 = 0; if (pcb == PCPU_GET(curpcb)) { /* * Clear the debug registers on the running * CPU, otherwise they will end up affecting * the next process we switch to. */ reset_dbregs(); } pcb->pcb_flags &= ~PCB_DBREGS; } /* * Arrange to trap the next fpu or `fwait' instruction (see fpu.c * for why fwait must be trapped at least if there is an fpu or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the fpu routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `fpu_exists' for testing the emulator on * systems with an fpu. */ load_cr0(rcr0() | CR0_MP | CR0_TS); /* Initialize the fpu (if any) for the current process. */ /* * XXX the above load_cr0() also initializes it and is a layering * violation. It drops the fpu state partially * and this would be fatal if we were interrupted now, and decided * to force the state to the pcb, and checked the invariant * (CR0_TS clear) if and only if PCPU_GET(fpcurthread) != NULL). * ALL of this can happen except the check. The check used to * happen and be fatal later when we didn't complete the drop * before returning to user mode. This should be fixed properly * soon. */ fpstate_drop(td); } void cpu_setregs(void) { register_t cr0; cr0 = rcr0(); cr0 |= CR0_NE; /* Done by fpuinit() */ cr0 |= CR0_MP | CR0_TS; /* Done at every execve() too. */ cr0 |= CR0_WP | CR0_AM; load_cr0(cr0); } static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ struct user_segment_descriptor gdt[NGDT * MAXCPU];/* global descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ static char dblfault_stack[PAGE_SIZE] __aligned(16); struct amd64tss common_tss[MAXCPU]; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, /* long */ 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_KPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 1, /* long */ 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_KPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 1, /* long */ 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GUCODE32_SEL 3 32 bit Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, /* long */ 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GUDATA_SEL 4 32/64 bit Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, /* long */ 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GUCODE_SEL 5 64 bit Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 1, /* long */ 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 6 Proc 0 Tss Descriptor */ { 0x0, /* segment base address */ sizeof(struct amd64tss)-1,/* length - all address space */ SDT_SYSTSS, /* segment type */ SEL_KPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, /* long */ 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Actually, the TSS is a system descriptor which is double size */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, /* long */ 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, ist) int idx; inthand_t *func; int typ; int dpl; int ist; { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = (uintptr_t)func; ip->gd_selector = GSEL(GCODE_SEL, SEL_KPL); ip->gd_ist = ist; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((uintptr_t)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(xmm), IDTVEC(dblfault), IDTVEC(fast_syscall), IDTVEC(fast_syscall32); void sdtossd(sd, ssd) struct user_segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_long = sd->sd_long; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } void ssdtosd(ssd, sd) struct soft_segment_descriptor *ssd; struct user_segment_descriptor *sd; { sd->sd_lobase = (ssd->ssd_base) & 0xffffff; sd->sd_hibase = (ssd->ssd_base >> 24) & 0xff; sd->sd_lolimit = (ssd->ssd_limit) & 0xffff; sd->sd_hilimit = (ssd->ssd_limit >> 16) & 0xf; sd->sd_type = ssd->ssd_type; sd->sd_dpl = ssd->ssd_dpl; sd->sd_p = ssd->ssd_p; sd->sd_long = ssd->ssd_long; sd->sd_def32 = ssd->ssd_def32; sd->sd_gran = ssd->ssd_gran; } void ssdtosyssd(ssd, sd) struct soft_segment_descriptor *ssd; struct system_segment_descriptor *sd; { sd->sd_lobase = (ssd->ssd_base) & 0xffffff; sd->sd_hibase = (ssd->ssd_base >> 24) & 0xfffffffffful; sd->sd_lolimit = (ssd->ssd_limit) & 0xffff; sd->sd_hilimit = (ssd->ssd_limit >> 16) & 0xf; sd->sd_type = ssd->ssd_type; sd->sd_dpl = ssd->ssd_dpl; sd->sd_p = ssd->ssd_p; sd->sd_gran = ssd->ssd_gran; } #if !defined(DEV_ATPIC) && defined(DEV_ISA) #include u_int isa_irq_pending(void) { return (0); } #endif #define PHYSMAP_SIZE (2 * 8) struct bios_smap { u_int64_t base; u_int64_t length; u_int32_t type; } __packed; u_int basemem; /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. * * XXX first should be vm_paddr_t. */ static void getmemsize(caddr_t kmdp, u_int64_t first) { int i, physmap_idx, pa_indx; vm_paddr_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t *pte; char *cp; struct bios_smap *smapbase, *smap, *smapend; u_int32_t smapsize; bzero(physmap, sizeof(physmap)); basemem = 0; physmap_idx = 0; /* * get memory map from INT 15:E820, kindly supplied by the loader. * * subr_module.c says: * "Consumer may safely assume that size value precedes data." * ie: an int32_t immediately precedes smap. */ smapbase = (struct bios_smap *)preload_search_info(kmdp, MODINFO_METADATA | MODINFOMD_SMAP); if (smapbase == NULL) panic("No BIOS smap info from loader!"); smapsize = *((u_int32_t *)smapbase - 1); smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize); for (smap = smapbase; smap < smapend; smap++) { if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%016lx len=%016lx\n", smap->type, smap->base, smap->length); if (smap->type != 0x01) continue; if (smap->length == 0) continue; for (i = 0; i <= physmap_idx; i += 2) { if (smap->base < physmap[i + 1]) { if (boothowto & RB_VERBOSE) printf( "Overlapping or non-montonic memory region, ignoring second region\n"); goto next_run; } } if (smap->base == physmap[physmap_idx + 1]) { physmap[physmap_idx + 1] += smap->length; next_run: continue; } physmap_idx += 2; if (physmap_idx == PHYSMAP_SIZE) { printf( "Too many segments in the physical address map, giving up\n"); break; } physmap[physmap_idx] = smap->base; physmap[physmap_idx + 1] = smap->base + smap->length; } /* * Find the 'base memory' segment for SMP */ basemem = 0; for (i = 0; i <= physmap_idx; i += 2) { if (physmap[i] == 0x00000000) { basemem = physmap[i + 1] / 1024; break; } } if (basemem == 0) panic("BIOS smap did not include a basemem segment!"); #ifdef SMP /* make hole for AP bootstrap code */ physmap[1] = mp_bootaddress(physmap[1] / 1024); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif /* * hw.physmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ cp = getenv("hw.physmem"); if (cp != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(cp, &ep, 0); if ((ep != cp) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", cp); else Maxmem = atop(AllowMem); freeenv(cp); } if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %ldK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa((vm_paddr_t)Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(&first); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; pte = CMAP1; /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_paddr_t end; end = ptoa((vm_paddr_t)Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad; int *ptr = (int *)CADDR1; /* * block out kernel memory as not available. */ if (pa >= 0x100000 && pa < first) continue; page_bad = FALSE; /* * map page into kernel: valid, read/write,non-cacheable */ *pte = pa | PG_V | PG_RW | PG_N; invltlb(); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) page_bad = TRUE; /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) page_bad = TRUE; /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) page_bad = TRUE; /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) page_bad = TRUE; /* * Restore original value. */ *(int *)ptr = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) continue; /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf( "Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; } } *pte = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; } u_int64_t hammer_time(u_int64_t modulep, u_int64_t physfree) { caddr_t kmdp; int gsel_tss, off, x; struct pcpu *pc; u_int64_t msr; char *env; #ifdef DEV_ISA /* Preemptively mask the atpics and leave them shut down */ outb(IO_ICU1 + ICU_IMR_OFFSET, 0xff); outb(IO_ICU2 + ICU_IMR_OFFSET, 0xff); #else #error "have you forgotten the isa device?"; #endif proc0.p_uarea = (struct user *)(physfree + KERNBASE); bzero(proc0.p_uarea, UAREA_PAGES * PAGE_SIZE); physfree += UAREA_PAGES * PAGE_SIZE; thread0.td_kstack = physfree + KERNBASE; bzero((void *)thread0.td_kstack, KSTACK_PAGES * PAGE_SIZE); physfree += KSTACK_PAGES * PAGE_SIZE; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; /* * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ proc_linkup(&proc0, &ksegrp0, &kse0, &thread0); preload_metadata = (caddr_t)(uintptr_t)(modulep + KERNBASE); preload_bootstrap_relocate(KERNBASE); kmdp = preload_search_by_type("elf kernel"); if (kmdp == NULL) kmdp = preload_search_by_type("elf64 kernel"); boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int); kern_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *) + KERNBASE; #ifdef DDB ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, void *); ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, void *); #endif /* Init basic tunables, hz etc */ init_param1(); /* * make gdt memory segments */ gdt_segs[GPROC0_SEL].ssd_base = (uintptr_t)&common_tss[0]; for (x = 0; x < NGDT; x++) { if (x != GPROC0_SEL && x != (GPROC0_SEL + 1)) ssdtosd(&gdt_segs[x], &gdt[x]); } ssdtosyssd(&gdt_segs[GPROC0_SEL], (struct system_segment_descriptor *)&gdt[GPROC0_SEL]); r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (long) gdt; lgdt(&r_gdt); pc = &__pcpu[0]; wrmsr(MSR_FSBASE, 0); /* User value */ wrmsr(MSR_GSBASE, (u_int64_t)pc); wrmsr(MSR_KGSBASE, 0); /* User value while in the kernel */ pcpu_init(pc, 0, sizeof(struct pcpu)); PCPU_SET(prvspace, pc); PCPU_SET(curthread, &thread0); PCPU_SET(curpcb, thread0.td_pcb); PCPU_SET(tssp, &common_tss[0]); /* * Initialize mutexes. * * icu_lock: in order to allow an interrupt to occur in a critical * section, to set pcpu->ipending (etc...) properly, we * must be able to get the icu lock, so it can't be * under witness. */ mutex_init(); mtx_init(&clock_lock, "clk", NULL, MTX_SPIN); mtx_init(&icu_lock, "icu", NULL, MTX_SPIN | MTX_NOWITNESS); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_DE, &IDTVEC(div), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_DB, &IDTVEC(dbg), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_NMI, &IDTVEC(nmi), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_BP, &IDTVEC(bpt), SDT_SYSIGT, SEL_UPL, 0); setidt(IDT_OF, &IDTVEC(ofl), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_BR, &IDTVEC(bnd), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_UD, &IDTVEC(ill), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_NM, &IDTVEC(dna), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_DF, &IDTVEC(dblfault), SDT_SYSIGT, SEL_KPL, 1); setidt(IDT_FPUGP, &IDTVEC(fpusegm), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_TS, &IDTVEC(tss), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_NP, &IDTVEC(missing), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_SS, &IDTVEC(stk), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_GP, &IDTVEC(prot), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_PF, &IDTVEC(page), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_MF, &IDTVEC(fpu), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_AC, &IDTVEC(align), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_MC, &IDTVEC(mchk), SDT_SYSIGT, SEL_KPL, 0); setidt(IDT_XF, &IDTVEC(xmm), SDT_SYSIGT, SEL_KPL, 0); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (long) idt; lidt(&r_idt); /* * Initialize the console before we print anything out. */ cninit(); #ifdef DEV_ATPIC atpic_startup(); #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif identify_cpu(); /* Final stage of CPU initialization */ initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ common_tss[0].tss_rsp0 = thread0.td_kstack + \ KSTACK_PAGES * PAGE_SIZE - sizeof(struct pcb); /* Ensure the stack is aligned to 16 bytes */ common_tss[0].tss_rsp0 &= ~0xFul; PCPU_SET(rsp0, common_tss[0].tss_rsp0); /* doublefault stack space, runs on ist1 */ common_tss[0].tss_ist1 = (long)&dblfault_stack[sizeof(dblfault_stack)]; gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); ltr(gsel_tss); /* Set up the fast syscall stuff */ msr = rdmsr(MSR_EFER) | EFER_SCE; wrmsr(MSR_EFER, msr); wrmsr(MSR_LSTAR, (u_int64_t)IDTVEC(fast_syscall)); wrmsr(MSR_CSTAR, (u_int64_t)IDTVEC(fast_syscall32)); msr = ((u_int64_t)GSEL(GCODE_SEL, SEL_KPL) << 32) | ((u_int64_t)GSEL(GUCODE32_SEL, SEL_UPL) << 48); wrmsr(MSR_STAR, msr); wrmsr(MSR_SF_MASK, PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D); getmemsize(kmdp, physfree); init_param2(physmem); /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off); msgbufinit(msgbufp, MSGBUF_SIZE); fpuinit(); /* transfer to user mode */ _ucodesel = GSEL(GUCODE_SEL, SEL_UPL); _udatasel = GSEL(GUDATA_SEL, SEL_UPL); _ucode32sel = GSEL(GUCODE32_SEL, SEL_UPL); /* setup proc 0's pcb */ thread0.td_pcb->pcb_flags = 0; /* XXXKSE */ thread0.td_pcb->pcb_cr3 = KPML4phys; thread0.td_frame = &proc0_tf; env = getenv("kernelname"); if (env != NULL) strlcpy(kernelname, env, sizeof(kernelname)); /* Location of kernel stack for locore */ return ((u_int64_t)thread0.td_pcb); } void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) { pcpu->pc_acpi_id = 0xffffffff; } int ptrace_set_pc(struct thread *td, unsigned long addr) { td->td_frame->tf_rip = addr; return (0); } int ptrace_single_step(struct thread *td) { td->td_frame->tf_rflags |= PSL_T; return (0); } int fill_regs(struct thread *td, struct reg *regs) { struct pcb *pcb; struct trapframe *tp; tp = td->td_frame; regs->r_r15 = tp->tf_r15; regs->r_r14 = tp->tf_r14; regs->r_r13 = tp->tf_r13; regs->r_r12 = tp->tf_r12; regs->r_r11 = tp->tf_r11; regs->r_r10 = tp->tf_r10; regs->r_r9 = tp->tf_r9; regs->r_r8 = tp->tf_r8; regs->r_rdi = tp->tf_rdi; regs->r_rsi = tp->tf_rsi; regs->r_rbp = tp->tf_rbp; regs->r_rbx = tp->tf_rbx; regs->r_rdx = tp->tf_rdx; regs->r_rcx = tp->tf_rcx; regs->r_rax = tp->tf_rax; regs->r_rip = tp->tf_rip; regs->r_cs = tp->tf_cs; regs->r_rflags = tp->tf_rflags; regs->r_rsp = tp->tf_rsp; regs->r_ss = tp->tf_ss; pcb = td->td_pcb; return (0); } int set_regs(struct thread *td, struct reg *regs) { struct pcb *pcb; struct trapframe *tp; tp = td->td_frame; if (!EFL_SECURE(regs->r_rflags, tp->tf_rflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_r15 = regs->r_r15; tp->tf_r14 = regs->r_r14; tp->tf_r13 = regs->r_r13; tp->tf_r12 = regs->r_r12; tp->tf_r11 = regs->r_r11; tp->tf_r10 = regs->r_r10; tp->tf_r9 = regs->r_r9; tp->tf_r8 = regs->r_r8; tp->tf_rdi = regs->r_rdi; tp->tf_rsi = regs->r_rsi; tp->tf_rbp = regs->r_rbp; tp->tf_rbx = regs->r_rbx; tp->tf_rdx = regs->r_rdx; tp->tf_rcx = regs->r_rcx; tp->tf_rax = regs->r_rax; tp->tf_rip = regs->r_rip; tp->tf_cs = regs->r_cs; tp->tf_rflags = regs->r_rflags; tp->tf_rsp = regs->r_rsp; tp->tf_ss = regs->r_ss; pcb = td->td_pcb; return (0); } /* XXX check all this stuff! */ /* externalize from sv_xmm */ static void fill_fpregs_xmm(struct savefpu *sv_xmm, struct fpreg *fpregs) { struct envxmm *penv_fpreg = (struct envxmm *)&fpregs->fpr_env; struct envxmm *penv_xmm = &sv_xmm->sv_env; int i; /* pcb -> fpregs */ bzero(fpregs, sizeof(*fpregs)); /* FPU control/status */ penv_fpreg->en_cw = penv_xmm->en_cw; penv_fpreg->en_sw = penv_xmm->en_sw; penv_fpreg->en_tw = penv_xmm->en_tw; penv_fpreg->en_opcode = penv_xmm->en_opcode; penv_fpreg->en_rip = penv_xmm->en_rip; penv_fpreg->en_rdp = penv_xmm->en_rdp; penv_fpreg->en_mxcsr = penv_xmm->en_mxcsr; penv_fpreg->en_mxcsr_mask = penv_xmm->en_mxcsr_mask; /* FPU registers */ for (i = 0; i < 8; ++i) bcopy(sv_xmm->sv_fp[i].fp_acc.fp_bytes, fpregs->fpr_acc[i], 10); /* SSE registers */ for (i = 0; i < 16; ++i) bcopy(sv_xmm->sv_xmm[i].xmm_bytes, fpregs->fpr_xacc[i], 16); } /* internalize from fpregs into sv_xmm */ static void set_fpregs_xmm(struct fpreg *fpregs, struct savefpu *sv_xmm) { struct envxmm *penv_xmm = &sv_xmm->sv_env; struct envxmm *penv_fpreg = (struct envxmm *)&fpregs->fpr_env; int i; /* fpregs -> pcb */ /* FPU control/status */ penv_xmm->en_cw = penv_fpreg->en_cw; penv_xmm->en_sw = penv_fpreg->en_sw; penv_xmm->en_tw = penv_fpreg->en_tw; penv_xmm->en_opcode = penv_fpreg->en_opcode; penv_xmm->en_rip = penv_fpreg->en_rip; penv_xmm->en_rdp = penv_fpreg->en_rdp; penv_xmm->en_mxcsr = penv_fpreg->en_mxcsr; penv_xmm->en_mxcsr_mask = penv_fpreg->en_mxcsr_mask; /* FPU registers */ for (i = 0; i < 8; ++i) bcopy(fpregs->fpr_acc[i], sv_xmm->sv_fp[i].fp_acc.fp_bytes, 10); /* SSE registers */ for (i = 0; i < 16; ++i) bcopy(fpregs->fpr_xacc[i], sv_xmm->sv_xmm[i].xmm_bytes, 16); } /* externalize from td->pcb */ int fill_fpregs(struct thread *td, struct fpreg *fpregs) { fill_fpregs_xmm(&td->td_pcb->pcb_save, fpregs); return (0); } /* internalize to td->pcb */ int set_fpregs(struct thread *td, struct fpreg *fpregs) { set_fpregs_xmm(fpregs, &td->td_pcb->pcb_save); return (0); } /* * Get machine context. */ int get_mcontext(struct thread *td, mcontext_t *mcp, int flags) { struct trapframe *tp; tp = td->td_frame; PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(tp->tf_rsp); PROC_UNLOCK(curthread->td_proc); mcp->mc_r15 = tp->tf_r15; mcp->mc_r14 = tp->tf_r14; mcp->mc_r13 = tp->tf_r13; mcp->mc_r12 = tp->tf_r12; mcp->mc_r11 = tp->tf_r11; mcp->mc_r10 = tp->tf_r10; mcp->mc_r9 = tp->tf_r9; mcp->mc_r8 = tp->tf_r8; mcp->mc_rdi = tp->tf_rdi; mcp->mc_rsi = tp->tf_rsi; mcp->mc_rbp = tp->tf_rbp; mcp->mc_rbx = tp->tf_rbx; mcp->mc_rcx = tp->tf_rcx; if (flags & GET_MC_CLEAR_RET) { mcp->mc_rax = 0; mcp->mc_rdx = 0; } else { mcp->mc_rax = tp->tf_rax; mcp->mc_rdx = tp->tf_rdx; } mcp->mc_rip = tp->tf_rip; mcp->mc_cs = tp->tf_cs; mcp->mc_rflags = tp->tf_rflags; mcp->mc_rsp = tp->tf_rsp; mcp->mc_ss = tp->tf_ss; mcp->mc_len = sizeof(*mcp); get_fpcontext(td, mcp); return (0); } /* * Set machine context. * * However, we don't set any but the user modifiable flags, and we won't * touch the cs selector. */ int set_mcontext(struct thread *td, const mcontext_t *mcp) { struct trapframe *tp; long rflags; int ret; tp = td->td_frame; if (mcp->mc_len != sizeof(*mcp)) return (EINVAL); rflags = (mcp->mc_rflags & PSL_USERCHANGE) | (tp->tf_rflags & ~PSL_USERCHANGE); ret = set_fpcontext(td, mcp); if (ret != 0) return (ret); tp->tf_r15 = mcp->mc_r15; tp->tf_r14 = mcp->mc_r14; tp->tf_r13 = mcp->mc_r13; tp->tf_r12 = mcp->mc_r12; tp->tf_r11 = mcp->mc_r11; tp->tf_r10 = mcp->mc_r10; tp->tf_r9 = mcp->mc_r9; tp->tf_r8 = mcp->mc_r8; tp->tf_rdi = mcp->mc_rdi; tp->tf_rsi = mcp->mc_rsi; tp->tf_rbp = mcp->mc_rbp; tp->tf_rbx = mcp->mc_rbx; tp->tf_rdx = mcp->mc_rdx; tp->tf_rcx = mcp->mc_rcx; tp->tf_rax = mcp->mc_rax; tp->tf_rip = mcp->mc_rip; tp->tf_rflags = rflags; tp->tf_rsp = mcp->mc_rsp; tp->tf_ss = mcp->mc_ss; return (0); } static void get_fpcontext(struct thread *td, mcontext_t *mcp) { mcp->mc_ownedfp = fpugetregs(td, (struct savefpu *)&mcp->mc_fpstate); mcp->mc_fpformat = fpuformat(); } static int set_fpcontext(struct thread *td, const mcontext_t *mcp) { if (mcp->mc_fpformat == _MC_FPFMT_NODEV) return (0); else if (mcp->mc_fpformat != _MC_FPFMT_XMM) return (EINVAL); else if (mcp->mc_ownedfp == _MC_FPOWNED_NONE) /* We don't care what state is left in the FPU or PCB. */ fpstate_drop(td); else if (mcp->mc_ownedfp == _MC_FPOWNED_FPU || mcp->mc_ownedfp == _MC_FPOWNED_PCB) { /* * XXX we violate the dubious requirement that fpusetregs() * be called with interrupts disabled. * XXX obsolete on trap-16 systems? */ fpusetregs(td, (struct savefpu *)&mcp->mc_fpstate); } else return (EINVAL); return (0); } void fpstate_drop(struct thread *td) { register_t s; s = intr_disable(); if (PCPU_GET(fpcurthread) == td) fpudrop(); /* * XXX force a full drop of the fpu. The above only drops it if we * owned it. * * XXX I don't much like fpugetregs()'s semantics of doing a full * drop. Dropping only to the pcb matches fnsave's behaviour. * We only need to drop to !PCB_INITDONE in sendsig(). But * sendsig() is the only caller of fpugetregs()... perhaps we just * have too many layers. */ curthread->td_pcb->pcb_flags &= ~PCB_FPUINITDONE; intr_restore(s); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { struct pcb *pcb; if (td == NULL) { dbregs->dr[0] = rdr0(); dbregs->dr[1] = rdr1(); dbregs->dr[2] = rdr2(); dbregs->dr[3] = rdr3(); dbregs->dr[6] = rdr6(); dbregs->dr[7] = rdr7(); } else { pcb = td->td_pcb; dbregs->dr[0] = pcb->pcb_dr0; dbregs->dr[1] = pcb->pcb_dr1; dbregs->dr[2] = pcb->pcb_dr2; dbregs->dr[3] = pcb->pcb_dr3; dbregs->dr[6] = pcb->pcb_dr6; dbregs->dr[7] = pcb->pcb_dr7; } dbregs->dr[4] = 0; dbregs->dr[5] = 0; dbregs->dr[8] = 0; dbregs->dr[9] = 0; dbregs->dr[10] = 0; dbregs->dr[11] = 0; dbregs->dr[12] = 0; dbregs->dr[13] = 0; dbregs->dr[14] = 0; dbregs->dr[15] = 0; return (0); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { struct pcb *pcb; int i; u_int64_t mask1, mask2; if (td == NULL) { load_dr0(dbregs->dr[0]); load_dr1(dbregs->dr[1]); load_dr2(dbregs->dr[2]); load_dr3(dbregs->dr[3]); load_dr6(dbregs->dr[6]); load_dr7(dbregs->dr[7]); } else { /* * Don't let an illegal value for dr7 get set. Specifically, * check for undefined settings. Setting these bit patterns * result in undefined behaviour and can lead to an unexpected * TRCTRAP or a general protection fault right here. */ for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; i++, mask1 <<= 2, mask2 <<= 2) if ((dbregs->dr[7] & mask1) == mask2) return (EINVAL); pcb = td->td_pcb; /* * Don't let a process set a breakpoint that is not within the * process's address space. If a process could do this, it * could halt the system by setting a breakpoint in the kernel * (if ddb was enabled). Thus, we need to check to make sure * that no breakpoints are being enabled for addresses outside * process's address space, unless, perhaps, we were called by * uid 0. * * XXX - what about when the watched area of the user's * address space is written into from within the kernel * ... wouldn't that still cause a breakpoint to be generated * from within kernel mode? */ if (suser(td) != 0) { if (dbregs->dr[7] & 0x3) { /* dr0 is enabled */ if (dbregs->dr[0] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & 0x3<<2) { /* dr1 is enabled */ if (dbregs->dr[1] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & 0x3<<4) { /* dr2 is enabled */ if (dbregs->dr[2] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & 0x3<<6) { /* dr3 is enabled */ if (dbregs->dr[3] >= VM_MAXUSER_ADDRESS) return (EINVAL); } } pcb->pcb_dr0 = dbregs->dr[0]; pcb->pcb_dr1 = dbregs->dr[1]; pcb->pcb_dr2 = dbregs->dr[2]; pcb->pcb_dr3 = dbregs->dr[3]; pcb->pcb_dr6 = dbregs->dr[6]; pcb->pcb_dr7 = dbregs->dr[7]; pcb->pcb_flags |= PCB_DBREGS; } return (0); } void reset_dbregs(void) { load_dr7(0); /* Turn off the control bits first */ load_dr0(0); load_dr1(0); load_dr2(0); load_dr3(0); load_dr6(0); } /* * Return > 0 if a hardware breakpoint has been hit, and the * breakpoint was in user space. Return 0, otherwise. */ int user_dbreg_trap(void) { u_int64_t dr7, dr6; /* debug registers dr6 and dr7 */ u_int64_t bp; /* breakpoint bits extracted from dr6 */ int nbp; /* number of breakpoints that triggered */ caddr_t addr[4]; /* breakpoint addresses */ int i; dr7 = rdr7(); if ((dr7 & 0x000000ff) == 0) { /* * all GE and LE bits in the dr7 register are zero, * thus the trap couldn't have been caused by the * hardware debug registers */ return 0; } nbp = 0; dr6 = rdr6(); bp = dr6 & 0x0000000f; if (!bp) { /* * None of the breakpoint bits are set meaning this * trap was not caused by any of the debug registers */ return 0; } /* * at least one of the breakpoints were hit, check to see * which ones and if any of them are user space addresses */ if (bp & 0x01) { addr[nbp++] = (caddr_t)rdr0(); } if (bp & 0x02) { addr[nbp++] = (caddr_t)rdr1(); } if (bp & 0x04) { addr[nbp++] = (caddr_t)rdr2(); } if (bp & 0x08) { addr[nbp++] = (caddr_t)rdr3(); } for (i=0; i, and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/compat/linux/linux_misc.c =================================================================== --- head/sys/compat/linux/linux_misc.c (revision 130343) +++ head/sys/compat/linux/linux_misc.c (revision 130344) @@ -1,1344 +1,1344 @@ /*- * Copyright (c) 1994-1995 Søren Schmidt * All rights reserved. * * 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 * in this position and unchanged. * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __i386__ #include #endif #ifdef __alpha__ #define BSD_TO_LINUX_SIGNAL(sig) (sig) #else #define BSD_TO_LINUX_SIGNAL(sig) \ (((sig) <= LINUX_SIGTBLSZ) ? bsd_to_linux_signal[_SIG_IDX(sig)] : sig) #endif #ifndef __alpha__ static unsigned int linux_to_bsd_resource[LINUX_RLIM_NLIMITS] = { RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_DATA, RLIMIT_STACK, RLIMIT_CORE, RLIMIT_RSS, RLIMIT_NPROC, RLIMIT_NOFILE, RLIMIT_MEMLOCK, -1 }; #endif /*!__alpha__*/ struct l_sysinfo { l_long uptime; /* Seconds since boot */ l_ulong loads[3]; /* 1, 5, and 15 minute load averages */ #define LINUX_SYSINFO_LOADS_SCALE 65536 l_ulong totalram; /* Total usable main memory size */ l_ulong freeram; /* Available memory size */ l_ulong sharedram; /* Amount of shared memory */ l_ulong bufferram; /* Memory used by buffers */ l_ulong totalswap; /* Total swap space size */ l_ulong freeswap; /* swap space still available */ l_ushort procs; /* Number of current processes */ l_ulong totalbig; l_ulong freebig; l_uint mem_unit; char _f[6]; /* Pads structure to 64 bytes */ }; #ifndef __alpha__ int linux_sysinfo(struct thread *td, struct linux_sysinfo_args *args) { struct l_sysinfo sysinfo; vm_object_t object; int i, j; struct timespec ts; /* Uptime is copied out of print_uptime() in kern_shutdown.c */ getnanouptime(&ts); i = 0; if (ts.tv_sec >= 86400) { ts.tv_sec %= 86400; i = 1; } if (i || ts.tv_sec >= 3600) { ts.tv_sec %= 3600; i = 1; } if (i || ts.tv_sec >= 60) { ts.tv_sec %= 60; i = 1; } sysinfo.uptime=ts.tv_sec; /* Use the information from the mib to get our load averages */ for (i = 0; i < 3; i++) sysinfo.loads[i] = averunnable.ldavg[i] * LINUX_SYSINFO_LOADS_SCALE / averunnable.fscale; sysinfo.totalram = physmem * PAGE_SIZE; sysinfo.freeram = sysinfo.totalram - cnt.v_wire_count * PAGE_SIZE; sysinfo.sharedram = 0; mtx_lock(&vm_object_list_mtx); TAILQ_FOREACH(object, &vm_object_list, object_list) if (object->shadow_count > 1) sysinfo.sharedram += object->resident_page_count; mtx_unlock(&vm_object_list_mtx); sysinfo.sharedram *= PAGE_SIZE; sysinfo.bufferram = 0; swap_pager_status(&i, &j); sysinfo.totalswap= i * PAGE_SIZE; sysinfo.freeswap = (i - j) * PAGE_SIZE; sysinfo.procs = nprocs; /* The following are only present in newer Linux kernels. */ sysinfo.totalbig = 0; sysinfo.freebig = 0; sysinfo.mem_unit = 1; return copyout(&sysinfo, args->info, sizeof(sysinfo)); } #endif /*!__alpha__*/ #ifndef __alpha__ int linux_alarm(struct thread *td, struct linux_alarm_args *args) { struct itimerval it, old_it; struct timeval tv; struct proc *p; #ifdef DEBUG if (ldebug(alarm)) printf(ARGS(alarm, "%u"), args->secs); #endif if (args->secs > 100000000) return EINVAL; it.it_value.tv_sec = (long)args->secs; it.it_value.tv_usec = 0; it.it_interval.tv_sec = 0; it.it_interval.tv_usec = 0; p = td->td_proc; PROC_LOCK(p); old_it = p->p_realtimer; getmicrouptime(&tv); if (timevalisset(&old_it.it_value)) callout_stop(&p->p_itcallout); if (it.it_value.tv_sec != 0) { callout_reset(&p->p_itcallout, tvtohz(&it.it_value), realitexpire, p); timevaladd(&it.it_value, &tv); } p->p_realtimer = it; PROC_UNLOCK(p); if (timevalcmp(&old_it.it_value, &tv, >)) { timevalsub(&old_it.it_value, &tv); if (old_it.it_value.tv_usec != 0) old_it.it_value.tv_sec++; td->td_retval[0] = old_it.it_value.tv_sec; } return 0; } #endif /*!__alpha__*/ int linux_brk(struct thread *td, struct linux_brk_args *args) { struct vmspace *vm = td->td_proc->p_vmspace; vm_offset_t new, old; struct obreak_args /* { char * nsize; } */ tmp; #ifdef DEBUG if (ldebug(brk)) printf(ARGS(brk, "%p"), (void *)args->dsend); #endif old = (vm_offset_t)vm->vm_daddr + ctob(vm->vm_dsize); new = (vm_offset_t)args->dsend; tmp.nsize = (char *) new; if (((caddr_t)new > vm->vm_daddr) && !obreak(td, &tmp)) td->td_retval[0] = (long)new; else td->td_retval[0] = (long)old; return 0; } int linux_uselib(struct thread *td, struct linux_uselib_args *args) { struct nameidata ni; struct vnode *vp; struct exec *a_out; struct vattr attr; vm_offset_t vmaddr; unsigned long file_offset; vm_offset_t buffer; unsigned long bss_size; char *library; int error; int locked; LCONVPATHEXIST(td, args->library, &library); #ifdef DEBUG if (ldebug(uselib)) printf(ARGS(uselib, "%s"), library); #endif a_out = NULL; locked = 0; vp = NULL; /* * XXX: This code should make use of vn_open(), rather than doing * all this stuff itself. */ NDINIT(&ni, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, library, td); error = namei(&ni); LFREEPATH(library); if (error) goto cleanup; vp = ni.ni_vp; /* * XXX - This looks like a bogus check. A LOCKLEAF namei should not * succeed without returning a vnode. */ if (vp == NULL) { error = ENOEXEC; /* ?? */ goto cleanup; } NDFREE(&ni, NDF_ONLY_PNBUF); /* * From here on down, we have a locked vnode that must be unlocked. */ locked++; /* Writable? */ if (vp->v_writecount) { error = ETXTBSY; goto cleanup; } /* Executable? */ error = VOP_GETATTR(vp, &attr, td->td_ucred, td); if (error) goto cleanup; if ((vp->v_mount->mnt_flag & MNT_NOEXEC) || ((attr.va_mode & 0111) == 0) || (attr.va_type != VREG)) { error = ENOEXEC; goto cleanup; } /* Sensible size? */ if (attr.va_size == 0) { error = ENOEXEC; goto cleanup; } /* Can we access it? */ error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); if (error) goto cleanup; /* * XXX: This should use vn_open() so that it is properly authorized, * and to reduce code redundancy all over the place here. */ #ifdef MAC error = mac_check_vnode_open(td->td_ucred, vp, FREAD); if (error) goto cleanup; #endif error = VOP_OPEN(vp, FREAD, td->td_ucred, td, -1); if (error) goto cleanup; /* Pull in executable header into kernel_map */ error = vm_mmap(kernel_map, (vm_offset_t *)&a_out, PAGE_SIZE, VM_PROT_READ, VM_PROT_READ, 0, (caddr_t)vp, 0); /* * Lock no longer needed */ locked = 0; VOP_UNLOCK(vp, 0, td); if (error) goto cleanup; /* Is it a Linux binary ? */ if (((a_out->a_magic >> 16) & 0xff) != 0x64) { error = ENOEXEC; goto cleanup; } /* * While we are here, we should REALLY do some more checks */ /* Set file/virtual offset based on a.out variant. */ switch ((int)(a_out->a_magic & 0xffff)) { case 0413: /* ZMAGIC */ file_offset = 1024; break; case 0314: /* QMAGIC */ file_offset = 0; break; default: error = ENOEXEC; goto cleanup; } bss_size = round_page(a_out->a_bss); /* Check various fields in header for validity/bounds. */ if (a_out->a_text & PAGE_MASK || a_out->a_data & PAGE_MASK) { error = ENOEXEC; goto cleanup; } /* text + data can't exceed file size */ if (a_out->a_data + a_out->a_text > attr.va_size) { error = EFAULT; goto cleanup; } /* * text/data/bss must not exceed limits * XXX - this is not complete. it should check current usage PLUS * the resources needed by this library. */ PROC_LOCK(td->td_proc); if (a_out->a_text > maxtsiz || a_out->a_data + bss_size > lim_cur(td->td_proc, RLIMIT_DATA)) { PROC_UNLOCK(td->td_proc); error = ENOMEM; goto cleanup; } PROC_UNLOCK(td->td_proc); mp_fixme("Unlocked vflags access."); /* prevent more writers */ vp->v_vflag |= VV_TEXT; /* * Check if file_offset page aligned. Currently we cannot handle * misalinged file offsets, and so we read in the entire image * (what a waste). */ if (file_offset & PAGE_MASK) { #ifdef DEBUG printf("uselib: Non page aligned binary %lu\n", file_offset); #endif /* Map text+data read/write/execute */ /* a_entry is the load address and is page aligned */ vmaddr = trunc_page(a_out->a_entry); /* get anon user mapping, read+write+execute */ error = vm_map_find(&td->td_proc->p_vmspace->vm_map, NULL, 0, &vmaddr, a_out->a_text + a_out->a_data, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); if (error) goto cleanup; /* map file into kernel_map */ error = vm_mmap(kernel_map, &buffer, round_page(a_out->a_text + a_out->a_data + file_offset), VM_PROT_READ, VM_PROT_READ, 0, (caddr_t)vp, trunc_page(file_offset)); if (error) goto cleanup; /* copy from kernel VM space to user space */ error = copyout((void *)(uintptr_t)(buffer + file_offset), (void *)vmaddr, a_out->a_text + a_out->a_data); /* release temporary kernel space */ vm_map_remove(kernel_map, buffer, buffer + round_page(a_out->a_text + a_out->a_data + file_offset)); if (error) goto cleanup; } else { #ifdef DEBUG printf("uselib: Page aligned binary %lu\n", file_offset); #endif /* * for QMAGIC, a_entry is 20 bytes beyond the load address * to skip the executable header */ vmaddr = trunc_page(a_out->a_entry); /* * Map it all into the process's space as a single * copy-on-write "data" segment. */ error = vm_mmap(&td->td_proc->p_vmspace->vm_map, &vmaddr, a_out->a_text + a_out->a_data, VM_PROT_ALL, VM_PROT_ALL, MAP_PRIVATE | MAP_FIXED, (caddr_t)vp, file_offset); if (error) goto cleanup; } #ifdef DEBUG printf("mem=%08lx = %08lx %08lx\n", (long)vmaddr, ((long*)vmaddr)[0], ((long*)vmaddr)[1]); #endif if (bss_size != 0) { /* Calculate BSS start address */ vmaddr = trunc_page(a_out->a_entry) + a_out->a_text + a_out->a_data; /* allocate some 'anon' space */ error = vm_map_find(&td->td_proc->p_vmspace->vm_map, NULL, 0, &vmaddr, bss_size, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); if (error) goto cleanup; } cleanup: /* Unlock vnode if needed */ if (locked) VOP_UNLOCK(vp, 0, td); /* Release the kernel mapping. */ if (a_out) vm_map_remove(kernel_map, (vm_offset_t)a_out, (vm_offset_t)a_out + PAGE_SIZE); return error; } int linux_select(struct thread *td, struct linux_select_args *args) { struct timeval tv0, tv1, utv, *tvp; int error; #ifdef DEBUG if (ldebug(select)) printf(ARGS(select, "%d, %p, %p, %p, %p"), args->nfds, (void *)args->readfds, (void *)args->writefds, (void *)args->exceptfds, (void *)args->timeout); #endif /* * Store current time for computation of the amount of * time left. */ if (args->timeout) { if ((error = copyin(args->timeout, &utv, sizeof(utv)))) goto select_out; #ifdef DEBUG if (ldebug(select)) printf(LMSG("incoming timeout (%ld/%ld)"), utv.tv_sec, utv.tv_usec); #endif if (itimerfix(&utv)) { /* * The timeval was invalid. Convert it to something * valid that will act as it does under Linux. */ utv.tv_sec += utv.tv_usec / 1000000; utv.tv_usec %= 1000000; if (utv.tv_usec < 0) { utv.tv_sec -= 1; utv.tv_usec += 1000000; } if (utv.tv_sec < 0) timevalclear(&utv); } microtime(&tv0); tvp = &utv; } else tvp = NULL; error = kern_select(td, args->nfds, args->readfds, args->writefds, args->exceptfds, tvp); #ifdef DEBUG if (ldebug(select)) printf(LMSG("real select returns %d"), error); #endif if (error) { /* * See fs/select.c in the Linux kernel. Without this, * Maelstrom doesn't work. */ if (error == ERESTART) error = EINTR; goto select_out; } if (args->timeout) { if (td->td_retval[0]) { /* * Compute how much time was left of the timeout, * by subtracting the current time and the time * before we started the call, and subtracting * that result from the user-supplied value. */ microtime(&tv1); timevalsub(&tv1, &tv0); timevalsub(&utv, &tv1); if (utv.tv_sec < 0) timevalclear(&utv); } else timevalclear(&utv); #ifdef DEBUG if (ldebug(select)) printf(LMSG("outgoing timeout (%ld/%ld)"), utv.tv_sec, utv.tv_usec); #endif if ((error = copyout(&utv, args->timeout, sizeof(utv)))) goto select_out; } select_out: #ifdef DEBUG if (ldebug(select)) printf(LMSG("select_out -> %d"), error); #endif return error; } int linux_mremap(struct thread *td, struct linux_mremap_args *args) { struct munmap_args /* { void *addr; size_t len; } */ bsd_args; int error = 0; #ifdef DEBUG if (ldebug(mremap)) printf(ARGS(mremap, "%p, %08lx, %08lx, %08lx"), (void *)args->addr, (unsigned long)args->old_len, (unsigned long)args->new_len, (unsigned long)args->flags); #endif args->new_len = round_page(args->new_len); args->old_len = round_page(args->old_len); if (args->new_len > args->old_len) { td->td_retval[0] = 0; return ENOMEM; } if (args->new_len < args->old_len) { bsd_args.addr = (caddr_t)(args->addr + args->new_len); bsd_args.len = args->old_len - args->new_len; error = munmap(td, &bsd_args); } td->td_retval[0] = error ? 0 : (uintptr_t)args->addr; return error; } #define LINUX_MS_ASYNC 0x0001 #define LINUX_MS_INVALIDATE 0x0002 #define LINUX_MS_SYNC 0x0004 int linux_msync(struct thread *td, struct linux_msync_args *args) { struct msync_args bsd_args; bsd_args.addr = (caddr_t)args->addr; bsd_args.len = args->len; bsd_args.flags = args->fl & ~LINUX_MS_SYNC; return msync(td, &bsd_args); } #ifndef __alpha__ int linux_time(struct thread *td, struct linux_time_args *args) { struct timeval tv; l_time_t tm; int error; #ifdef DEBUG if (ldebug(time)) printf(ARGS(time, "*")); #endif microtime(&tv); tm = tv.tv_sec; if (args->tm && (error = copyout(&tm, args->tm, sizeof(tm)))) return error; td->td_retval[0] = tm; return 0; } #endif /*!__alpha__*/ struct l_times_argv { l_long tms_utime; l_long tms_stime; l_long tms_cutime; l_long tms_cstime; }; #ifdef __alpha__ #define CLK_TCK 1024 /* Linux uses 1024 on alpha */ #else #define CLK_TCK 100 /* Linux uses 100 */ #endif #define CONVTCK(r) (r.tv_sec * CLK_TCK + r.tv_usec / (1000000 / CLK_TCK)) int linux_times(struct thread *td, struct linux_times_args *args) { struct timeval tv; struct l_times_argv tms; struct rusage ru; int error; #ifdef DEBUG if (ldebug(times)) printf(ARGS(times, "*")); #endif mtx_lock_spin(&sched_lock); calcru(td->td_proc, &ru.ru_utime, &ru.ru_stime, NULL); mtx_unlock_spin(&sched_lock); tms.tms_utime = CONVTCK(ru.ru_utime); tms.tms_stime = CONVTCK(ru.ru_stime); tms.tms_cutime = CONVTCK(td->td_proc->p_stats->p_cru.ru_utime); tms.tms_cstime = CONVTCK(td->td_proc->p_stats->p_cru.ru_stime); if ((error = copyout(&tms, args->buf, sizeof(tms)))) return error; microuptime(&tv); td->td_retval[0] = (int)CONVTCK(tv); return 0; } int linux_newuname(struct thread *td, struct linux_newuname_args *args) { struct l_new_utsname utsname; char osname[LINUX_MAX_UTSNAME]; char osrelease[LINUX_MAX_UTSNAME]; char *p; #ifdef DEBUG if (ldebug(newuname)) printf(ARGS(newuname, "*")); #endif linux_get_osname(td, osname); linux_get_osrelease(td, osrelease); bzero(&utsname, sizeof(utsname)); strlcpy(utsname.sysname, osname, LINUX_MAX_UTSNAME); getcredhostname(td->td_ucred, utsname.nodename, LINUX_MAX_UTSNAME); strlcpy(utsname.release, osrelease, LINUX_MAX_UTSNAME); strlcpy(utsname.version, version, LINUX_MAX_UTSNAME); for (p = utsname.version; *p != '\0'; ++p) if (*p == '\n') { *p = '\0'; break; } #ifdef __i386__ { const char *class; switch (cpu_class) { case CPUCLASS_686: class = "i686"; break; case CPUCLASS_586: class = "i586"; break; case CPUCLASS_486: class = "i486"; break; default: class = "i386"; } strlcpy(utsname.machine, class, LINUX_MAX_UTSNAME); } #else strlcpy(utsname.machine, machine, LINUX_MAX_UTSNAME); #endif strlcpy(utsname.domainname, domainname, LINUX_MAX_UTSNAME); return (copyout(&utsname, args->buf, sizeof(utsname))); } #if defined(__i386__) struct l_utimbuf { l_time_t l_actime; l_time_t l_modtime; }; int linux_utime(struct thread *td, struct linux_utime_args *args) { struct timeval tv[2], *tvp; struct l_utimbuf lut; char *fname; int error; LCONVPATHEXIST(td, args->fname, &fname); #ifdef DEBUG if (ldebug(utime)) printf(ARGS(utime, "%s, *"), fname); #endif if (args->times) { if ((error = copyin(args->times, &lut, sizeof lut))) { LFREEPATH(fname); return error; } tv[0].tv_sec = lut.l_actime; tv[0].tv_usec = 0; tv[1].tv_sec = lut.l_modtime; tv[1].tv_usec = 0; tvp = tv; } else tvp = NULL; error = kern_utimes(td, fname, UIO_SYSSPACE, tvp, UIO_SYSSPACE); LFREEPATH(fname); return (error); } #endif /* __i386__ */ #define __WCLONE 0x80000000 #ifndef __alpha__ int linux_waitpid(struct thread *td, struct linux_waitpid_args *args) { int error, options, tmpstat; #ifdef DEBUG if (ldebug(waitpid)) printf(ARGS(waitpid, "%d, %p, %d"), args->pid, (void *)args->status, args->options); #endif options = (args->options & (WNOHANG | WUNTRACED)); /* WLINUXCLONE should be equal to __WCLONE, but we make sure */ if (args->options & __WCLONE) options |= WLINUXCLONE; error = kern_wait(td, args->pid, &tmpstat, options, NULL); if (error) return error; if (args->status) { tmpstat &= 0xffff; if (WIFSIGNALED(tmpstat)) tmpstat = (tmpstat & 0xffffff80) | BSD_TO_LINUX_SIGNAL(WTERMSIG(tmpstat)); else if (WIFSTOPPED(tmpstat)) tmpstat = (tmpstat & 0xffff00ff) | (BSD_TO_LINUX_SIGNAL(WSTOPSIG(tmpstat)) << 8); return copyout(&tmpstat, args->status, sizeof(int)); } return 0; } #endif /*!__alpha__*/ int linux_wait4(struct thread *td, struct linux_wait4_args *args) { int error, options, tmpstat; struct rusage ru; struct proc *p; #ifdef DEBUG if (ldebug(wait4)) printf(ARGS(wait4, "%d, %p, %d, %p"), args->pid, (void *)args->status, args->options, (void *)args->rusage); #endif options = (args->options & (WNOHANG | WUNTRACED)); /* WLINUXCLONE should be equal to __WCLONE, but we make sure */ if (args->options & __WCLONE) options |= WLINUXCLONE; error = kern_wait(td, args->pid, &tmpstat, options, &ru); if (error) return error; p = td->td_proc; PROC_LOCK(p); SIGDELSET(p->p_siglist, SIGCHLD); PROC_UNLOCK(p); if (args->status) { tmpstat &= 0xffff; if (WIFSIGNALED(tmpstat)) tmpstat = (tmpstat & 0xffffff80) | BSD_TO_LINUX_SIGNAL(WTERMSIG(tmpstat)); else if (WIFSTOPPED(tmpstat)) tmpstat = (tmpstat & 0xffff00ff) | (BSD_TO_LINUX_SIGNAL(WSTOPSIG(tmpstat)) << 8); error = copyout(&tmpstat, args->status, sizeof(int)); } if (args->rusage != NULL && error == 0) error = copyout(&ru, args->rusage, sizeof(ru)); return (error); } int linux_mknod(struct thread *td, struct linux_mknod_args *args) { char *path; int error; LCONVPATHCREAT(td, args->path, &path); #ifdef DEBUG if (ldebug(mknod)) printf(ARGS(mknod, "%s, %d, %d"), path, args->mode, args->dev); #endif if (args->mode & S_IFIFO) error = kern_mkfifo(td, path, UIO_SYSSPACE, args->mode); else error = kern_mknod(td, path, UIO_SYSSPACE, args->mode, args->dev); LFREEPATH(path); return (error); } /* * UGH! This is just about the dumbest idea I've ever heard!! */ int linux_personality(struct thread *td, struct linux_personality_args *args) { #ifdef DEBUG if (ldebug(personality)) printf(ARGS(personality, "%lu"), (unsigned long)args->per); #endif #ifndef __alpha__ if (args->per != 0) return EINVAL; #endif /* Yes Jim, it's still a Linux... */ td->td_retval[0] = 0; return 0; } /* * Wrappers for get/setitimer for debugging.. */ int linux_setitimer(struct thread *td, struct linux_setitimer_args *args) { struct setitimer_args bsa; struct itimerval foo; int error; #ifdef DEBUG if (ldebug(setitimer)) printf(ARGS(setitimer, "%p, %p"), (void *)args->itv, (void *)args->oitv); #endif bsa.which = args->which; bsa.itv = (struct itimerval *)args->itv; bsa.oitv = (struct itimerval *)args->oitv; if (args->itv) { if ((error = copyin(args->itv, &foo, sizeof(foo)))) return error; #ifdef DEBUG if (ldebug(setitimer)) { printf("setitimer: value: sec: %ld, usec: %ld\n", foo.it_value.tv_sec, foo.it_value.tv_usec); printf("setitimer: interval: sec: %ld, usec: %ld\n", foo.it_interval.tv_sec, foo.it_interval.tv_usec); } #endif } return setitimer(td, &bsa); } int linux_getitimer(struct thread *td, struct linux_getitimer_args *args) { struct getitimer_args bsa; #ifdef DEBUG if (ldebug(getitimer)) printf(ARGS(getitimer, "%p"), (void *)args->itv); #endif bsa.which = args->which; bsa.itv = (struct itimerval *)args->itv; return getitimer(td, &bsa); } #ifndef __alpha__ int linux_nice(struct thread *td, struct linux_nice_args *args) { struct setpriority_args bsd_args; bsd_args.which = PRIO_PROCESS; bsd_args.who = 0; /* current process */ bsd_args.prio = args->inc; return setpriority(td, &bsd_args); } #endif /*!__alpha__*/ int linux_setgroups(struct thread *td, struct linux_setgroups_args *args) { struct ucred *newcred, *oldcred; l_gid_t linux_gidset[NGROUPS]; gid_t *bsd_gidset; int ngrp, error; struct proc *p; ngrp = args->gidsetsize; if (ngrp < 0 || ngrp >= NGROUPS) return (EINVAL); error = copyin(args->grouplist, linux_gidset, ngrp * sizeof(l_gid_t)); if (error) return (error); newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; /* * cr_groups[0] holds egid. Setting the whole set from * the supplied set will cause egid to be changed too. * Keep cr_groups[0] unchanged to prevent that. */ if ((error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); crfree(newcred); return (error); } crcopy(newcred, oldcred); if (ngrp > 0) { newcred->cr_ngroups = ngrp + 1; bsd_gidset = newcred->cr_groups; ngrp--; while (ngrp >= 0) { bsd_gidset[ngrp + 1] = linux_gidset[ngrp]; ngrp--; } } else newcred->cr_ngroups = 1; setsugid(p); p->p_ucred = newcred; PROC_UNLOCK(p); crfree(oldcred); return (0); } int linux_getgroups(struct thread *td, struct linux_getgroups_args *args) { struct ucred *cred; l_gid_t linux_gidset[NGROUPS]; gid_t *bsd_gidset; int bsd_gidsetsz, ngrp, error; cred = td->td_ucred; bsd_gidset = cred->cr_groups; bsd_gidsetsz = cred->cr_ngroups - 1; /* * cr_groups[0] holds egid. Returning the whole set * here will cause a duplicate. Exclude cr_groups[0] * to prevent that. */ if ((ngrp = args->gidsetsize) == 0) { td->td_retval[0] = bsd_gidsetsz; return (0); } if (ngrp < bsd_gidsetsz) return (EINVAL); ngrp = 0; while (ngrp < bsd_gidsetsz) { linux_gidset[ngrp] = bsd_gidset[ngrp + 1]; ngrp++; } if ((error = copyout(linux_gidset, args->grouplist, ngrp * sizeof(l_gid_t)))) return (error); td->td_retval[0] = ngrp; return (0); } #ifndef __alpha__ int linux_setrlimit(struct thread *td, struct linux_setrlimit_args *args) { struct rlimit bsd_rlim; struct l_rlimit rlim; u_int which; int error; #ifdef DEBUG if (ldebug(setrlimit)) printf(ARGS(setrlimit, "%d, %p"), args->resource, (void *)args->rlim); #endif if (args->resource >= LINUX_RLIM_NLIMITS) return (EINVAL); which = linux_to_bsd_resource[args->resource]; if (which == -1) return (EINVAL); error = copyin(args->rlim, &rlim, sizeof(rlim)); if (error) return (error); bsd_rlim.rlim_cur = (rlim_t)rlim.rlim_cur; bsd_rlim.rlim_max = (rlim_t)rlim.rlim_max; return (kern_setrlimit(td, which, &bsd_rlim)); } int linux_old_getrlimit(struct thread *td, struct linux_old_getrlimit_args *args) { struct l_rlimit rlim; struct proc *p = td->td_proc; struct rlimit bsd_rlim; u_int which; #ifdef DEBUG if (ldebug(old_getrlimit)) printf(ARGS(old_getrlimit, "%d, %p"), args->resource, (void *)args->rlim); #endif if (args->resource >= LINUX_RLIM_NLIMITS) return (EINVAL); which = linux_to_bsd_resource[args->resource]; if (which == -1) return (EINVAL); PROC_LOCK(p); lim_rlimit(p, which, &bsd_rlim); PROC_UNLOCK(p); rlim.rlim_cur = (unsigned long)bsd_rlim.rlim_cur; if (rlim.rlim_cur == ULONG_MAX) rlim.rlim_cur = LONG_MAX; rlim.rlim_max = (unsigned long)bsd_rlim.rlim_max; if (rlim.rlim_max == ULONG_MAX) rlim.rlim_max = LONG_MAX; return (copyout(&rlim, args->rlim, sizeof(rlim))); } int linux_getrlimit(struct thread *td, struct linux_getrlimit_args *args) { struct l_rlimit rlim; struct proc *p = td->td_proc; struct rlimit bsd_rlim; u_int which; #ifdef DEBUG if (ldebug(getrlimit)) printf(ARGS(getrlimit, "%d, %p"), args->resource, (void *)args->rlim); #endif if (args->resource >= LINUX_RLIM_NLIMITS) return (EINVAL); which = linux_to_bsd_resource[args->resource]; if (which == -1) return (EINVAL); PROC_LOCK(p); lim_rlimit(p, which, &bsd_rlim); PROC_UNLOCK(p); rlim.rlim_cur = (l_ulong)bsd_rlim.rlim_cur; rlim.rlim_max = (l_ulong)bsd_rlim.rlim_max; return (copyout(&rlim, args->rlim, sizeof(rlim))); } #endif /*!__alpha__*/ int linux_sched_setscheduler(struct thread *td, struct linux_sched_setscheduler_args *args) { struct sched_setscheduler_args bsd; #ifdef DEBUG if (ldebug(sched_setscheduler)) printf(ARGS(sched_setscheduler, "%d, %d, %p"), args->pid, args->policy, (const void *)args->param); #endif switch (args->policy) { case LINUX_SCHED_OTHER: bsd.policy = SCHED_OTHER; break; case LINUX_SCHED_FIFO: bsd.policy = SCHED_FIFO; break; case LINUX_SCHED_RR: bsd.policy = SCHED_RR; break; default: return EINVAL; } bsd.pid = args->pid; bsd.param = (struct sched_param *)args->param; return sched_setscheduler(td, &bsd); } int linux_sched_getscheduler(struct thread *td, struct linux_sched_getscheduler_args *args) { struct sched_getscheduler_args bsd; int error; #ifdef DEBUG if (ldebug(sched_getscheduler)) printf(ARGS(sched_getscheduler, "%d"), args->pid); #endif bsd.pid = args->pid; error = sched_getscheduler(td, &bsd); switch (td->td_retval[0]) { case SCHED_OTHER: td->td_retval[0] = LINUX_SCHED_OTHER; break; case SCHED_FIFO: td->td_retval[0] = LINUX_SCHED_FIFO; break; case SCHED_RR: td->td_retval[0] = LINUX_SCHED_RR; break; } return error; } int linux_sched_get_priority_max(struct thread *td, struct linux_sched_get_priority_max_args *args) { struct sched_get_priority_max_args bsd; #ifdef DEBUG if (ldebug(sched_get_priority_max)) printf(ARGS(sched_get_priority_max, "%d"), args->policy); #endif switch (args->policy) { case LINUX_SCHED_OTHER: bsd.policy = SCHED_OTHER; break; case LINUX_SCHED_FIFO: bsd.policy = SCHED_FIFO; break; case LINUX_SCHED_RR: bsd.policy = SCHED_RR; break; default: return EINVAL; } return sched_get_priority_max(td, &bsd); } int linux_sched_get_priority_min(struct thread *td, struct linux_sched_get_priority_min_args *args) { struct sched_get_priority_min_args bsd; #ifdef DEBUG if (ldebug(sched_get_priority_min)) printf(ARGS(sched_get_priority_min, "%d"), args->policy); #endif switch (args->policy) { case LINUX_SCHED_OTHER: bsd.policy = SCHED_OTHER; break; case LINUX_SCHED_FIFO: bsd.policy = SCHED_FIFO; break; case LINUX_SCHED_RR: bsd.policy = SCHED_RR; break; default: return EINVAL; } return sched_get_priority_min(td, &bsd); } #define REBOOT_CAD_ON 0x89abcdef #define REBOOT_CAD_OFF 0 #define REBOOT_HALT 0xcdef0123 int linux_reboot(struct thread *td, struct linux_reboot_args *args) { struct reboot_args bsd_args; #ifdef DEBUG if (ldebug(reboot)) printf(ARGS(reboot, "0x%x"), args->cmd); #endif if (args->cmd == REBOOT_CAD_ON || args->cmd == REBOOT_CAD_OFF) return (0); bsd_args.opt = (args->cmd == REBOOT_HALT) ? RB_HALT : 0; return (reboot(td, &bsd_args)); } #ifndef __alpha__ /* * The FreeBSD native getpid(2), getgid(2) and getuid(2) also modify - * td->td_retval[1] when COMPAT_43 or COMPAT_SUNOS is defined. This + * td->td_retval[1] when COMPAT_43 is defined. This * globbers registers that are assumed to be preserved. The following * lightweight syscalls fixes this. See also linux_getgid16() and * linux_getuid16() in linux_uid16.c. * * linux_getpid() - MP SAFE * linux_getgid() - MP SAFE * linux_getuid() - MP SAFE */ int linux_getpid(struct thread *td, struct linux_getpid_args *args) { td->td_retval[0] = td->td_proc->p_pid; return (0); } int linux_getgid(struct thread *td, struct linux_getgid_args *args) { td->td_retval[0] = td->td_ucred->cr_rgid; return (0); } int linux_getuid(struct thread *td, struct linux_getuid_args *args) { td->td_retval[0] = td->td_ucred->cr_ruid; return (0); } #endif /*!__alpha__*/ int linux_getsid(struct thread *td, struct linux_getsid_args *args) { struct getsid_args bsd; bsd.pid = args->pid; return getsid(td, &bsd); } Index: head/sys/compat/linux/linux_uid16.c =================================================================== --- head/sys/compat/linux/linux_uid16.c (revision 130343) +++ head/sys/compat/linux/linux_uid16.c (revision 130344) @@ -1,292 +1,292 @@ /*- * Copyright (c) 2001 The FreeBSD Project * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include DUMMY(setfsuid16); DUMMY(setfsgid16); DUMMY(getresuid16); DUMMY(getresgid16); #define CAST_NOCHG(x) ((x == 0xFFFF) ? -1 : x) int linux_chown16(struct thread *td, struct linux_chown16_args *args) { char *path; int error; LCONVPATHEXIST(td, args->path, &path); #ifdef DEBUG if (ldebug(chown16)) printf(ARGS(chown16, "%s, %d, %d"), path, args->uid, args->gid); #endif error = kern_chown(td, path, UIO_SYSSPACE, CAST_NOCHG(args->uid), CAST_NOCHG(args->gid)); LFREEPATH(path); return (error); } int linux_lchown16(struct thread *td, struct linux_lchown16_args *args) { char *path; int error; LCONVPATHEXIST(td, args->path, &path); #ifdef DEBUG if (ldebug(lchown16)) printf(ARGS(lchown16, "%s, %d, %d"), path, args->uid, args->gid); #endif error = kern_lchown(td, path, UIO_SYSSPACE, CAST_NOCHG(args->uid), CAST_NOCHG(args->gid)); LFREEPATH(path); return (error); } int linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args) { struct ucred *newcred, *oldcred; l_gid16_t linux_gidset[NGROUPS]; gid_t *bsd_gidset; int ngrp, error; struct proc *p; #ifdef DEBUG if (ldebug(setgroups16)) printf(ARGS(setgroups16, "%d, *"), args->gidsetsize); #endif ngrp = args->gidsetsize; if (ngrp < 0 || ngrp >= NGROUPS) return (EINVAL); error = copyin(args->gidset, linux_gidset, ngrp * sizeof(l_gid16_t)); if (error) return (error); newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; /* * cr_groups[0] holds egid. Setting the whole set from * the supplied set will cause egid to be changed too. * Keep cr_groups[0] unchanged to prevent that. */ if ((error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); crfree(newcred); return (error); } crcopy(newcred, oldcred); if (ngrp > 0) { newcred->cr_ngroups = ngrp + 1; bsd_gidset = newcred->cr_groups; ngrp--; while (ngrp >= 0) { bsd_gidset[ngrp + 1] = linux_gidset[ngrp]; ngrp--; } } else newcred->cr_ngroups = 1; setsugid(td->td_proc); p->p_ucred = newcred; PROC_UNLOCK(p); crfree(oldcred); return (0); } int linux_getgroups16(struct thread *td, struct linux_getgroups16_args *args) { struct ucred *cred; l_gid16_t linux_gidset[NGROUPS]; gid_t *bsd_gidset; int bsd_gidsetsz, ngrp, error; #ifdef DEBUG if (ldebug(getgroups16)) printf(ARGS(getgroups16, "%d, *"), args->gidsetsize); #endif cred = td->td_ucred; bsd_gidset = cred->cr_groups; bsd_gidsetsz = cred->cr_ngroups - 1; /* * cr_groups[0] holds egid. Returning the whole set * here will cause a duplicate. Exclude cr_groups[0] * to prevent that. */ if ((ngrp = args->gidsetsize) == 0) { td->td_retval[0] = bsd_gidsetsz; return (0); } if (ngrp < bsd_gidsetsz) return (EINVAL); ngrp = 0; while (ngrp < bsd_gidsetsz) { linux_gidset[ngrp] = bsd_gidset[ngrp + 1]; ngrp++; } error = copyout(linux_gidset, args->gidset, ngrp * sizeof(l_gid16_t)); if (error) return (error); td->td_retval[0] = ngrp; return (0); } /* * The FreeBSD native getgid(2) and getuid(2) also modify td->td_retval[1] - * when COMPAT_43 or COMPAT_SUNOS is defined. This globbers registers that + * when COMPAT_43 is defined. This globbers registers that * are assumed to be preserved. The following lightweight syscalls fixes * this. See also linux_getpid(2), linux_getgid(2) and linux_getuid(2) in * linux_misc.c * * linux_getgid16() - MP SAFE * linux_getuid16() - MP SAFE */ int linux_getgid16(struct thread *td, struct linux_getgid16_args *args) { td->td_retval[0] = td->td_ucred->cr_rgid; return (0); } int linux_getuid16(struct thread *td, struct linux_getuid16_args *args) { td->td_retval[0] = td->td_ucred->cr_ruid; return (0); } int linux_getegid16(struct thread *td, struct linux_getegid16_args *args) { struct getegid_args bsd; return (getegid(td, &bsd)); } int linux_geteuid16(struct thread *td, struct linux_geteuid16_args *args) { struct geteuid_args bsd; return (geteuid(td, &bsd)); } int linux_setgid16(struct thread *td, struct linux_setgid16_args *args) { struct setgid_args bsd; bsd.gid = args->gid; return (setgid(td, &bsd)); } int linux_setuid16(struct thread *td, struct linux_setuid16_args *args) { struct setuid_args bsd; bsd.uid = args->uid; return (setuid(td, &bsd)); } int linux_setregid16(struct thread *td, struct linux_setregid16_args *args) { struct setregid_args bsd; bsd.rgid = CAST_NOCHG(args->rgid); bsd.egid = CAST_NOCHG(args->egid); return (setregid(td, &bsd)); } int linux_setreuid16(struct thread *td, struct linux_setreuid16_args *args) { struct setreuid_args bsd; bsd.ruid = CAST_NOCHG(args->ruid); bsd.euid = CAST_NOCHG(args->euid); return (setreuid(td, &bsd)); } int linux_setresgid16(struct thread *td, struct linux_setresgid16_args *args) { struct setresgid_args bsd; bsd.rgid = CAST_NOCHG(args->rgid); bsd.egid = CAST_NOCHG(args->egid); bsd.sgid = CAST_NOCHG(args->sgid); return (setresgid(td, &bsd)); } int linux_setresuid16(struct thread *td, struct linux_setresuid16_args *args) { struct setresuid_args bsd; bsd.ruid = CAST_NOCHG(args->ruid); bsd.euid = CAST_NOCHG(args->euid); bsd.suid = CAST_NOCHG(args->suid); return (setresuid(td, &bsd)); } Index: head/sys/conf/NOTES =================================================================== --- head/sys/conf/NOTES (revision 130343) +++ head/sys/conf/NOTES (revision 130344) @@ -1,2410 +1,2404 @@ # $FreeBSD$ # # NOTES -- Lines that can be cut/pasted into kernel and hints configs. # # Lines that begin with 'device', 'options', 'machine', 'ident', 'maxusers', # 'makeoptions', 'hints', etc. go into the kernel configuration that you # run config(8) with. # # Lines that begin with 'hint.' are NOT for config(8), they go into your # hints file. See /boot/device.hints and/or the 'hints' config(8) directive. # # Please use ``make LINT'' to create an old-style LINT file if you want to # do kernel test-builds. # # This file contains machine independent kernel configuration notes. For # machine dependent notes, look in /sys//conf/NOTES. # # # NOTES conventions and style guide: # # Large block comments should begin and end with a line containing only a # comment character. # # To describe a particular object, a block comment (if it exists) should # come first. Next should come device, options, and hints lines in that # order. All device and option lines must be described by a comment that # doesn't just expand the device or option name. Use only a concise # comment on the same line if possible. Very detailed descriptions of # devices and subsystems belong in man pages. # # A space followed by a tab separates 'options' from an option name. Two # spaces followed by a tab separate 'device' from a device name. Comments # after an option or device should use one space after the comment character. # To comment out a negative option that disables code and thus should not be # enabled for LINT builds, precede 'options' with "#!". # # # This is the ``identification'' of the kernel. Usually this should # be the same as the name of your kernel. # ident LINT # # The `maxusers' parameter controls the static sizing of a number of # internal system tables by a formula defined in subr_param.c. # Omitting this parameter or setting it to 0 will cause the system to # auto-size based on physical memory. # maxusers 10 # # The `makeoptions' parameter allows variables to be passed to the # generated Makefile in the build area. # # CONF_CFLAGS gives some extra compiler flags that are added to ${CFLAGS} # after most other flags. Here we use it to inhibit use of non-optimal # gcc builtin functions (e.g., memcmp). # # DEBUG happens to be magic. # The following is equivalent to 'config -g KERNELNAME' and creates # 'kernel.debug' compiled with -g debugging as well as a normal # 'kernel'. Use 'make install.debug' to install the debug kernel # but that isn't normally necessary as the debug symbols are not loaded # by the kernel and are not useful there anyway. # # KERNEL can be overridden so that you can change the default name of your # kernel. # # MODULES_OVERRIDE can be used to limit modules built to a specific list. # makeoptions CONF_CFLAGS=-fno-builtin #Don't allow use of memcmp, etc. #makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols #makeoptions KERNEL=foo #Build kernel "foo" and install "/foo" # Only build Linux API modules and plus those parts of the sound system I need. #makeoptions MODULES_OVERRIDE="linux sound/snd sound/pcm sound/driver/maestro3" makeoptions DESTDIR=/tmp # # Certain applications can grow to be larger than the 512M limit # that FreeBSD initially imposes. Below are some options to # allow that limit to grow to 1GB, and can be increased further # with changing the parameters. MAXDSIZ is the maximum that the # limit can be set to, and the DFLDSIZ is the default value for # the limit. MAXSSIZ is the maximum that the stack limit can be # set to. You might want to set the default lower than the max, # and explicitly set the maximum with a shell command for processes # that regularly exceed the limit like INND. # options MAXDSIZ=(1024UL*1024*1024) options MAXSSIZ=(128UL*1024*1024) options DFLDSIZ=(1024UL*1024*1024) # # BLKDEV_IOSIZE sets the default block size used in user block # device I/O. Note that this value will be overriden by the label # when specifying a block device from a label with a non-0 # partition blocksize. The default is PAGE_SIZE. # options BLKDEV_IOSIZE=8192 # Options for the VM subsystem # L2 cache size (in KB) can be specified in PQ_CACHESIZE options PQ_CACHESIZE=512 # color for 512k cache # Deprecated options supported for backwards compatibility #options PQ_NOOPT # No coloring #options PQ_LARGECACHE # color for 512k cache #options PQ_HUGECACHE # color for 1024k cache #options PQ_MEDIUMCACHE # color for 256k cache #options PQ_NORMALCACHE # color for 64k cache # This allows you to actually store this configuration file into # the kernel binary itself, where it may be later read by saying: # strings -n 3 /boot/kernel/kernel | sed -n 's/^___//p' > MYKERNEL # options INCLUDE_CONFIG_FILE # Include this file in kernel options GEOM_AES # Don't use, use GEOM_BDE options GEOM_APPLE # Apple partitioning options GEOM_BDE # Disk encryption. options GEOM_BSD # BSD disklabels options GEOM_CONCAT # Disk concatenation. options GEOM_FOX # Redundant path mitigation options GEOM_GATE # Userland services. options GEOM_GPT # GPT partitioning options GEOM_MBR # DOS/MBR partitioning options GEOM_NOP # Test class. options GEOM_PC98 # NEC PC9800 partitioning options GEOM_STRIPE # Disk striping. options GEOM_SUNLABEL # Sun/Solaris partitioning options GEOM_VOL # Volume names from UFS superblock # # The root device and filesystem type can be compiled in; # this provides a fallback option if the root device cannot # be correctly guessed by the bootstrap code, or an override if # the RB_DFLTROOT flag (-r) is specified when booting the kernel. # options ROOTDEVNAME=\"ufs:da0s2e\" ##################################################################### # Scheduler options: # # Specifying one of SCHED_4BSD or SCHED_ULE is mandatory. These options # select which scheduler is compiled in. # # SCHED_4BSD is the historical, proven, BSD scheduler. It has a global run # queue and no cpu affinity which makes it suboptimal for SMP. It has very # good interactivity and priority selection. # # SCHED_ULE is a new scheduler that has been designed for SMP and has some # advantages for UP as well. It is intended to replace the 4BSD scheduler # over time. # options SCHED_4BSD #options SCHED_ULE ##################################################################### # SMP OPTIONS: # # SMP enables building of a Symmetric MultiProcessor Kernel. # Mandatory: options SMP # Symmetric MultiProcessor Kernel # ADAPTIVE_MUTEXES changes the behavior of blocking mutexes to spin # if the thread that currently owns the mutex is executing on another # CPU. options ADAPTIVE_MUTEXES # MUTEX_NOINLINE forces mutex operations to call functions to perform each # operation rather than inlining the simple cases. This can be used to # shrink the size of the kernel text segment. Note that this behavior is # already implied by the INVARIANT_SUPPORT, INVARIANTS, MUTEX_PROFILING, # and WITNESS options. options MUTEX_NOINLINE # SMP Debugging Options: # # MUTEX_DEBUG enables various extra assertions in the mutex code. # WITNESS enables the witness code which detects deadlocks and cycles # during locking operations. # WITNESS_DDB causes the witness code to drop into the kernel debugger if # a lock heirarchy violation occurs or if locks are held when going to # sleep. # WITNESS_SKIPSPIN disables the witness checks on spin mutexes. options MUTEX_DEBUG options WITNESS options WITNESS_DDB options WITNESS_SKIPSPIN # MUTEX_PROFILING - Profiling mutual exclusion locks (mutexes). See # MUTEX_PROFILING(9) for details. options MUTEX_PROFILING ##################################################################### # COMPATIBILITY OPTIONS # # Implement system calls compatible with 4.3BSD and older versions of # FreeBSD. You probably do NOT want to remove this as much current code # still relies on the 4.3 emulation. Note that some architectures that # are supported by FreeBSD do not include support for certain important # aspects of this compatibility option, namely those related to the # signal delivery mechanism. # options COMPAT_43 -# -# Be compatible with SunOS. The COMPAT_43 option above pulls in most -# (all?) of the changes that this option turns on. -# -options COMPAT_SUNOS - # Enable FreeBSD4 compatibility syscalls options COMPAT_FREEBSD4 # # These three options provide support for System V Interface # Definition-style interprocess communication, in the form of shared # memory, semaphores, and message queues, respectively. # options SYSVSHM options SYSVSEM options SYSVMSG ##################################################################### # DEBUGGING OPTIONS # # Enable the kernel debugger. # options DDB # # Use direct symbol lookup routines for ddb instead of the kernel linker # ones, so that symbols (mostly) work before the kernel linker has been # initialized. This is not the default because it breaks ddb's lookup of # symbols in loaded modules. # #!options DDB_NOKLDSYM # # Print the numerical value of symbols in addition to the symbolic # representation. # options DDB_NUMSYM # # Print a stack trace of the current thread out on the console for a panic. # options DDB_TRACE # # Don't drop into DDB for a panic. Intended for unattended operation # where you may want to drop to DDB from the console, but still want # the machine to recover from a panic # options DDB_UNATTENDED # # If using GDB remote mode to debug the kernel, there's a non-standard # extension to the remote protocol that can be used to use the serial # port as both the debugging port and the system console. It's non- # standard and you're on your own if you enable it. See also the # "remotechat" variables in the FreeBSD specific version of gdb. # options GDB_REMOTE_CHAT # # KTRACE enables the system-call tracing facility ktrace(2). To be more # SMP-friendly, KTRACE uses a worker thread to process most trace events # asynchronously to the thread generating the event. This requires a # pre-allocated store of objects representing trace events. The # KTRACE_REQUEST_POOL option specifies the initial size of this store. # The size of the pool can be adjusted both at boottime and runtime via # the kern.ktrace_request_pool tunable and sysctl. # options KTRACE #kernel tracing options KTRACE_REQUEST_POOL=101 # # KTR is a kernel tracing mechanism imported from BSD/OS. Currently it # has no userland interface aside from a few sysctl's. It is enabled with # the KTR option. KTR_ENTRIES defines the number of entries in the circular # trace buffer. KTR_COMPILE defines the mask of events to compile into the # kernel as defined by the KTR_* constants in . KTR_MASK defines the # initial value of the ktr_mask variable which determines at runtime what # events to trace. KTR_CPUMASK determines which CPU's log events, with # bit X corresponding to cpu X. KTR_VERBOSE enables dumping of KTR events # to the console by default. This functionality can be toggled via the # debug.ktr_verbose sysctl and defaults to off if KTR_VERBOSE is not defined. # options KTR options KTR_ENTRIES=1024 options KTR_COMPILE=(KTR_INTR|KTR_PROC) options KTR_MASK=KTR_INTR options KTR_CPUMASK=0x3 options KTR_VERBOSE # # The INVARIANTS option is used in a number of source files to enable # extra sanity checking of internal structures. This support is not # enabled by default because of the extra time it would take to check # for these conditions, which can only occur as a result of # programming errors. # options INVARIANTS # # The INVARIANT_SUPPORT option makes us compile in support for # verifying some of the internal structures. It is a prerequisite for # 'INVARIANTS', as enabling 'INVARIANTS' will make these functions be # called. The intent is that you can set 'INVARIANTS' for single # source files (by changing the source file or specifying it on the # command line) if you have 'INVARIANT_SUPPORT' enabled. Also, if you # wish to build a kernel module with 'INVARIANTS', then adding # 'INVARIANT_SUPPORT' to your kernel will provide all the necessary # infrastructure without the added overhead. # options INVARIANT_SUPPORT # # The DIAGNOSTIC option is used to enable extra debugging information # from some parts of the kernel. As this makes everything more noisy, # it is disabled by default. # options DIAGNOSTIC # # REGRESSION causes optional kernel interfaces necessary only for regression # testing to be enabled. These interfaces may consitute security risks # when enabled, as they permit processes to easily modify aspects of the # run-time environment to reproduce unlikely or unusual (possibly normally # impossible) scenarios. # options REGRESSION # # RESTARTABLE_PANICS allows one to continue from a panic as if it were # a call to the debugger via the Debugger() function instead. It is only # useful if a kernel debugger is present. To restart from a panic, reset # the panicstr variable to NULL and continue execution. This option is # for development use only and should NOT be used in production systems # to "workaround" a panic. # #options RESTARTABLE_PANICS # # This option let some drivers co-exist that can't co-exist in a running # system. This is used to be able to compile all kernel code in one go for # quality assurance purposes (like this file, which the option takes it name # from.) # options COMPILING_LINT ##################################################################### # NETWORKING OPTIONS # # Protocol families: # Only the INET (Internet) family is officially supported in FreeBSD. # options INET #Internet communications protocols options INET6 #IPv6 communications protocols options IPSEC #IP security options IPSEC_ESP #IP security (crypto; define w/ IPSEC) options IPSEC_DEBUG #debug for IP security # # Set IPSEC_FILTERGIF to force packets coming through a gif tunnel # to be processed by any configured packet filtering (ipfw, ipf). # The default is that packets coming from a tunnel are _not_ processed; # they are assumed trusted. # # Note that enabling this can be problematic as there are no mechanisms # in place for distinguishing packets coming out of a tunnel (e.g. no # encX devices as found on openbsd). # #options IPSEC_FILTERGIF #filter ipsec packets from a tunnel #options FAST_IPSEC #new IPsec (cannot define w/ IPSEC) options IPX #IPX/SPX communications protocols options IPXIP #IPX in IP encapsulation (not available) #options NCP #NetWare Core protocol options NETATALK #Appletalk communications protocols options NETATALKDEBUG #Appletalk debugging # # SMB/CIFS requester # NETSMB enables support for SMB protocol, it requires LIBMCHAIN and LIBICONV # options. # NETSMBCRYPTO enables support for encrypted passwords. options NETSMB #SMB/CIFS requester options NETSMBCRYPTO #encrypted password support for SMB # mchain library. It can be either loaded as KLD or compiled into kernel options LIBMCHAIN # netgraph(4). Enable the base netgraph code with the NETGRAPH option. # Individual node types can be enabled with the corresponding option # listed below; however, this is not strictly necessary as netgraph # will automatically load the corresponding KLD module if the node type # is not already compiled into the kernel. Each type below has a # corresponding man page, e.g., ng_async(8). options NETGRAPH #netgraph(4) system options NETGRAPH_ASYNC options NETGRAPH_ATMLLC options NETGRAPH_ATM_ATMPIF options NETGRAPH_BLUETOOTH # ng_bluetooth(4) options NETGRAPH_BLUETOOTH_BT3C # ng_bt3c(4) options NETGRAPH_BLUETOOTH_H4 # ng_h4(4) options NETGRAPH_BLUETOOTH_HCI # ng_hci(4) options NETGRAPH_BLUETOOTH_L2CAP # ng_l2cap(4) options NETGRAPH_BLUETOOTH_SOCKET # ng_btsocket(4) options NETGRAPH_BLUETOOTH_UBT # ng_ubt(4) options NETGRAPH_BLUETOOTH_UBTBCMFW # ubtbcmfw(4) options NETGRAPH_BPF options NETGRAPH_BRIDGE options NETGRAPH_CISCO options NETGRAPH_ECHO options NETGRAPH_ETHER options NETGRAPH_FRAME_RELAY options NETGRAPH_GIF options NETGRAPH_GIF_DEMUX options NETGRAPH_HOLE options NETGRAPH_IFACE options NETGRAPH_IP_INPUT options NETGRAPH_KSOCKET options NETGRAPH_L2TP options NETGRAPH_LMI # MPPC compression requires proprietary files (not included) #options NETGRAPH_MPPC_COMPRESSION options NETGRAPH_MPPC_ENCRYPTION options NETGRAPH_ONE2MANY options NETGRAPH_PPP options NETGRAPH_PPPOE options NETGRAPH_PPTPGRE options NETGRAPH_RFC1490 options NETGRAPH_SOCKET options NETGRAPH_SPLIT options NETGRAPH_SPPP options NETGRAPH_TEE options NETGRAPH_TTY options NETGRAPH_UI options NETGRAPH_VJC # NgATM - Netgraph ATM options NGATM_ATM options NGATM_ATMBASE options NGATM_SSCOP options NGATM_SSCFU options NGATM_UNI device mn # Munich32x/Falc54 Nx64kbit/sec cards. device musycc # LMC/SBE LMC1504 quad T1/E1 # # Network interfaces: # The `loop' device is MANDATORY when networking is enabled. # The `ether' device provides generic code to handle # Ethernets; it is MANDATORY when an Ethernet device driver is # configured or token-ring is enabled. # The `wlan' device provides generic code to support 802.11 # drivers, including host AP mode; it is MANDATORY for the wi # driver and will eventually be required by all 802.11 drivers. # The `fddi' device provides generic code to support FDDI. # The `arcnet' device provides generic code to support Arcnet. # The `sppp' device serves a similar role for certain types # of synchronous PPP links (like `cx', `ar'). # The `sl' device implements the Serial Line IP (SLIP) service. # The `ppp' device implements the Point-to-Point Protocol. # The `bpf' device enables the Berkeley Packet Filter. Be # aware of the legal and administrative consequences of enabling this # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. # The `disc' device implements a minimal network interface, # which throws away all packets sent and never receives any. It is # included for testing purposes. This shows up as the `ds' interface. # The `tap' device is a pty-like virtual Ethernet interface # The `tun' device implements (user-)ppp and nos-tun # The `gif' device implements IPv6 over IP4 tunneling, # IPv4 over IPv6 tunneling, IPv4 over IPv4 tunneling and # IPv6 over IPv6 tunneling. # The `gre' device implements two types of IP4 over IP4 tunneling: # GRE and MOBILE, as specified in the RFC1701 and RFC2004. # The XBONEHACK option allows the same pair of addresses to be configured on # multiple gif interfaces. # The `faith' device captures packets sent to it and diverts them # to the IPv4/IPv6 translation daemon. # The `stf' device implements 6to4 encapsulation. # The `ef' device provides support for multiple ethernet frame types # specified via ETHER_* options. See ef(4) for details. # # The pf packet filter consists of three devices: # The `pf' device provides /dev/pf and the firewall code itself. # The `pflog' device provides the pflog0 interface which logs packets. # The `pfsync' device provides the pfsync0 interface used for # synchronization of firewall state tables (over the net). # Requires option PFIL_HOOKS and (when used as a module) option RANDOM_IP_ID # # The PPP_BSDCOMP option enables support for compress(1) style entire # packet compression, the PPP_DEFLATE is for zlib/gzip style compression. # PPP_FILTER enables code for filtering the ppp data stream and selecting # events for resetting the demand dial activity timer - requires bpf. # See pppd(8) for more details. # device ether #Generic Ethernet device vlan #VLAN support device wlan #802.11 support device token #Generic TokenRing device fddi #Generic FDDI device arcnet #Generic Arcnet device sppp #Generic Synchronous PPP device loop #Network loopback device device bpf #Berkeley packet filter device disc #Discard device (ds0, ds1, etc) device tap #Virtual Ethernet driver device tun #Tunnel driver (ppp(8), nos-tun(8)) device sl #Serial Line IP device gre #IP over IP tunneling device pf #PF OpenBSD packet-filter firewall device pflog #logging support interface for PF device pfsync #synchronization interface for PF device ppp #Point-to-point protocol options PPP_BSDCOMP #PPP BSD-compress support options PPP_DEFLATE #PPP zlib/deflate/gzip support options PPP_FILTER #enable bpf filtering (needs bpf) device ef # Multiple ethernet frames support options ETHER_II # enable Ethernet_II frame options ETHER_8023 # enable Ethernet_802.3 (Novell) frame options ETHER_8022 # enable Ethernet_802.2 frame options ETHER_SNAP # enable Ethernet_802.2/SNAP frame # for IPv6 device gif #IPv6 and IPv4 tunneling options XBONEHACK device faith #for IPv6 and IPv4 translation device stf #6to4 IPv6 over IPv4 encapsulation # # Internet family options: # # MROUTING enables the kernel multicast packet forwarder, which works # with mrouted(8). # # PIM enables Protocol Independent Multicast in the kernel. # Requires MROUTING enabled. # # IPFIREWALL enables support for IP firewall construction, in # conjunction with the `ipfw' program. IPFIREWALL_VERBOSE sends # logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT # limits the number of times a matching entry can be logged. # # WARNING: IPFIREWALL defaults to a policy of "deny ip from any to any" # and if you do not add other rules during startup to allow access, # YOU WILL LOCK YOURSELF OUT. It is suggested that you set firewall_type=open # in /etc/rc.conf when first enabling this feature, then refining the # firewall rules in /etc/rc.firewall after you've tested that the new kernel # feature works properly. # # IPFIREWALL_DEFAULT_TO_ACCEPT causes the default rule (at boot) to # allow everything. Use with care, if a cracker can crash your # firewall machine, they can get to your protected machines. However, # if you are using it as an as-needed filter for specific problems as # they arise, then this may be for you. Changing the default to 'allow' # means that you won't get stuck if the kernel and /sbin/ipfw binary get # out of sync. # # IPDIVERT enables the divert IP sockets, used by ``ipfw divert'' # # IPSTEALTH enables code to support stealth forwarding (i.e., forwarding # packets without touching the ttl). This can be useful to hide firewalls # from traceroute and similar tools. # # PFIL_HOOKS enables an abtraction layer which is meant to be used in # network code where filtering is required. See pfil(9). This option is # required by the IPFILTER option and the PF device. # # TCPDEBUG enables code which keeps traces of the TCP state machine # for sockets with the SO_DEBUG option set, which can then be examined # using the trpt(8) utility. # options MROUTING # Multicast routing options PIM # Protocol Independent Multicast options IPFIREWALL #firewall options IPFIREWALL_VERBOSE #enable logging to syslogd(8) options IPFIREWALL_VERBOSE_LIMIT=100 #limit verbosity options IPFIREWALL_DEFAULT_TO_ACCEPT #allow everything by default options IPV6FIREWALL #firewall for IPv6 options IPV6FIREWALL_VERBOSE options IPV6FIREWALL_VERBOSE_LIMIT=100 options IPV6FIREWALL_DEFAULT_TO_ACCEPT options IPDIVERT #divert sockets options IPFILTER #ipfilter support options IPFILTER_LOG #ipfilter logging options IPFILTER_DEFAULT_BLOCK #block all packets by default options IPSTEALTH #support for stealth forwarding options PFIL_HOOKS #required by IPFILTER options TCPDEBUG # The MBUF_STRESS_TEST option enables options which create # various random failures / extreme cases related to mbuf # functions. See mbuf(9) for a list of available test cases. options MBUF_STRESS_TEST # RANDOM_IP_ID causes the ID field in IP packets to be randomized # instead of incremented by 1 with each packet generated. This # option closes a minor information leak which allows remote # observers to determine the rate of packet generation on the # machine by watching the counter. options RANDOM_IP_ID # Statically Link in accept filters options ACCEPT_FILTER_DATA options ACCEPT_FILTER_HTTP # TCP_DROP_SYNFIN adds support for ignoring TCP packets with SYN+FIN. This # prevents nmap et al. from identifying the TCP/IP stack, but breaks support # for RFC1644 extensions and is not recommended for web servers. # options TCP_DROP_SYNFIN #drop TCP packets with SYN+FIN # TCP_SIGNATURE adds support for RFC 2385 (TCP-MD5) digests. These are # carried in TCP option 19. This option is commonly used to protect # TCP sessions (e.g. BGP) where IPSEC is not available nor desirable. # This is enabled on a per-socket basis using the TCP_MD5SIG socket option. # This requires the use of 'device crypto', 'options FAST_IPSEC', and # 'device cryptodev' as it depends on the non-KAME IPSEC SADB code. #options TCP_SIGNATURE #include support for RFC 2385 # DUMMYNET enables the "dummynet" bandwidth limiter. You need IPFIREWALL # as well. See dummynet(4) and ipfw(8) for more info. When you run # DUMMYNET it is advisable to also have "options HZ=1000" to achieve a # smoother scheduling of the traffic. # # BRIDGE enables bridging between ethernet cards -- see bridge(4). # You can use IPFIREWALL and DUMMYNET together with bridging. # options DUMMYNET options BRIDGE # Zero copy sockets support. This enables "zero copy" for sending and # receving data via a socket. The send side works for any type of NIC, # the receive side only works for NICs that support MTUs greater than the # page size of your architecture and that support header splitting. See # zero_copy(9) for more details. options ZERO_COPY_SOCKETS # # ATM (HARP version) options # # ATM_CORE includes the base ATM functionality code. This must be included # for ATM support. # # ATM_IP includes support for running IP over ATM. # # At least one (and usually only one) of the following signalling managers # must be included (note that all signalling managers include PVC support): # ATM_SIGPVC includes support for the PVC-only signalling manager `sigpvc'. # ATM_SPANS includes support for the `spans' signalling manager, which runs # the FORE Systems's proprietary SPANS signalling protocol. # ATM_UNI includes support for the `uni30' and `uni31' signalling managers, # which run the ATM Forum UNI 3.x signalling protocols. # # The `hfa' driver provides support for the FORE Systems, Inc. # PCA-200E ATM PCI Adapter. # # The `harp' pseudo-driver makes all NATM interface drivers available to HARP. # options ATM_CORE #core ATM protocol family options ATM_IP #IP over ATM support options ATM_SIGPVC #SIGPVC signalling manager options ATM_SPANS #SPANS signalling manager options ATM_UNI #UNI signalling manager device hfa #FORE PCA-200E ATM PCI device harp #Pseudo-interface for NATM ##################################################################### # FILESYSTEM OPTIONS # # Only the root, /usr, and /tmp filesystems need be statically # compiled; everything else will be automatically loaded at mount # time. (Exception: the UFS family--- FFS --- cannot # currently be demand-loaded.) Some people still prefer to statically # compile other filesystems as well. # # NB: The NULL, PORTAL, UMAP and UNION filesystems are known to be # buggy, and WILL panic your system if you attempt to do anything with # them. They are included here as an incentive for some enterprising # soul to sit down and fix them. # # One of these is mandatory: options FFS #Fast filesystem options NFSCLIENT #Network File System client # The rest are optional: options CD9660 #ISO 9660 filesystem options FDESCFS #File descriptor filesystem options HPFS #OS/2 File system options MSDOSFS #MS DOS File System (FAT, FAT32) options NFSSERVER #Network File System server options NTFS #NT File System options NULLFS #NULL filesystem # Broken (depends on NCP): #options NWFS #NetWare filesystem options PORTALFS #Portal filesystem options PROCFS #Process filesystem (requires PSEUDOFS) options PSEUDOFS #Pseudo-filesystem framework options SMBFS #SMB/CIFS filesystem options UDF #Universal Disk Format # Broken (seriously (functionally) broken): #options UMAPFS #UID map filesystem options UNIONFS #Union filesystem # The xFS_ROOT options REQUIRE the associated ``options xFS'' options NFS_ROOT #NFS usable as root device # Soft updates is a technique for improving filesystem speed and # making abrupt shutdown less risky. # options SOFTUPDATES # Extended attributes allow additional data to be associated with files, # and is used for ACLs, Capabilities, and MAC labels. # See src/sys/ufs/ufs/README.extattr for more information. options UFS_EXTATTR options UFS_EXTATTR_AUTOSTART # Access Control List support for UFS filesystems. The current ACL # implementation requires extended attribute support, UFS_EXTATTR, # for the underlying filesystem. # See src/sys/ufs/ufs/README.acls for more information. options UFS_ACL # Directory hashing improves the speed of operations on very large # directories at the expense of some memory. options UFS_DIRHASH # Make space in the kernel for a root filesystem on a md device. # Define to the number of kilobytes to reserve for the filesystem. options MD_ROOT_SIZE=10 # Make the md device a potential root device, either with preloaded # images of type mfs_root or md_root. options MD_ROOT # Disk quotas are supported when this option is enabled. options QUOTA #enable disk quotas # If you are running a machine just as a fileserver for PC and MAC # users, using SAMBA or Netatalk, you may consider setting this option # and keeping all those users' directories on a filesystem that is # mounted with the suiddir option. This gives new files the same # ownership as the directory (similar to group). It's a security hole # if you let these users run programs, so confine it to file-servers # (but it'll save you lots of headaches in those cases). Root owned # directories are exempt and X bits are cleared. The suid bit must be # set on the directory as well; see chmod(1) PC owners can't see/set # ownerships so they keep getting their toes trodden on. This saves # you all the support calls as the filesystem it's used on will act as # they expect: "It's my dir so it must be my file". # options SUIDDIR # NFS options: options NFS_MINATTRTIMO=3 # VREG attrib cache timeout in sec options NFS_MAXATTRTIMO=60 options NFS_MINDIRATTRTIMO=30 # VDIR attrib cache timeout in sec options NFS_MAXDIRATTRTIMO=60 options NFS_GATHERDELAY=10 # Default write gather delay (msec) options NFS_WDELAYHASHSIZ=16 # and with this options NFS_DEBUG # Enable NFS Debugging # Coda stuff: options CODA #CODA filesystem. device vcoda 4 #coda minicache <-> venus comm. # Use the old Coda 5.x venus<->kernel interface instead of the new # realms-aware 6.x protocol. #options CODA_COMPAT_5 # # Add support for the EXT2FS filesystem of Linux fame. Be a bit # careful with this - the ext2fs code has a tendency to lag behind # changes and not be exercised very much, so mounting read/write could # be dangerous (and even mounting read only could result in panics.) # options EXT2FS # Use real implementations of the aio_* system calls. There are numerous # stability and security issues in the current aio code that make it # unsuitable for inclusion on machines with untrusted local users. options VFS_AIO # Cryptographically secure random number generator; /dev/[u]random device random # Optional character code conversion support with LIBICONV. # Each option requires their base file system and LIBICONV. options CD9660_ICONV options MSDOSFS_ICONV options NTFS_ICONV options UDF_ICONV ##################################################################### # POSIX P1003.1B # Real time extensions added in the 1993 Posix # _KPOSIX_PRIORITY_SCHEDULING: Build in _POSIX_PRIORITY_SCHEDULING options _KPOSIX_PRIORITY_SCHEDULING # p1003_1b_semaphores are very experimental, # user should be ready to assist in debugging if problems arise. options P1003_1B_SEMAPHORES ##################################################################### # SECURITY POLICY PARAMETERS # Support for Mandatory Access Control (MAC): options MAC options MAC_BIBA options MAC_BSDEXTENDED options MAC_DEBUG options MAC_IFOFF options MAC_LOMAC options MAC_MLS options MAC_NONE options MAC_PARTITION options MAC_PORTACL options MAC_SEEOTHERUIDS options MAC_STUB options MAC_TEST ##################################################################### # CLOCK OPTIONS # The granularity of operation is controlled by the kernel option HZ whose # default value (100) means a granularity of 10ms (1s/HZ). # Some subsystems, such as DUMMYNET, might benefit from a smaller # granularity such as 1ms or less, for a smoother scheduling of packets. # Consider, however, that reducing the granularity too much might # cause excessive overhead in clock interrupt processing, # potentially causing ticks to be missed and thus actually reducing # the accuracy of operation. options HZ=100 # Enable support for the kernel PLL to use an external PPS signal, # under supervision of [x]ntpd(8) # More info in ntpd documentation: http://www.eecis.udel.edu/~ntp options PPS_SYNC ##################################################################### # SCSI DEVICES # SCSI DEVICE CONFIGURATION # The SCSI subsystem consists of the `base' SCSI code, a number of # high-level SCSI device `type' drivers, and the low-level host-adapter # device drivers. The host adapters are listed in the ISA and PCI # device configuration sections below. # # It is possible to wire down your SCSI devices so that a given bus, # target, and LUN always come on line as the same device unit. In # earlier versions the unit numbers were assigned in the order that # the devices were probed on the SCSI bus. This means that if you # removed a disk drive, you may have had to rewrite your /etc/fstab # file, and also that you had to be careful when adding a new disk # as it may have been probed earlier and moved your device configuration # around. (See also option GEOM_VOL for a different solution to this # problem.) # This old behavior is maintained as the default behavior. The unit # assignment begins with the first non-wired down unit for a device # type. For example, if you wire a disk as "da3" then the first # non-wired disk will be assigned da4. # The syntax for wiring down devices is: hint.scbus.0.at="ahc0" hint.scbus.1.at="ahc1" hint.scbus.1.bus="0" hint.scbus.3.at="ahc2" hint.scbus.3.bus="0" hint.scbus.2.at="ahc2" hint.scbus.2.bus="1" hint.da.0.at="scbus0" hint.da.0.target="0" hint.da.0.unit="0" hint.da.1.at="scbus3" hint.da.1.target="1" hint.da.2.at="scbus2" hint.da.2.target="3" hint.sa.1.at="scbus1" hint.sa.1.target="6" # "units" (SCSI logical unit number) that are not specified are # treated as if specified as LUN 0. # All SCSI devices allocate as many units as are required. # The ch driver drives SCSI Media Changer ("jukebox") devices. # # The da driver drives SCSI Direct Access ("disk") and Optical Media # ("WORM") devices. # # The sa driver drives SCSI Sequential Access ("tape") devices. # # The cd driver drives SCSI Read Only Direct Access ("cd") devices. # # The ses driver drives SCSI Envinronment Services ("ses") and # SAF-TE ("SCSI Accessable Fault-Tolerant Enclosure") devices. # # The pt driver drives SCSI Processor devices. # # # Target Mode support is provided here but also requires that a SIM # (SCSI Host Adapter Driver) provide support as well. # # The targ driver provides target mode support as a Processor type device. # It exists to give the minimal context necessary to respond to Inquiry # commands. There is a sample user application that shows how the rest # of the command support might be done in /usr/share/examples/scsi_target. # # The targbh driver provides target mode support and exists to respond # to incoming commands that do not otherwise have a logical unit assigned # to them. # # The "unknown" device (uk? in pre-2.0.5) is now part of the base SCSI # configuration as the "pass" driver. device scbus #base SCSI code device ch #SCSI media changers device da #SCSI direct access devices (aka disks) device sa #SCSI tapes device cd #SCSI CD-ROMs device ses #SCSI Environmental Services (and SAF-TE) device pt #SCSI processor device targ #SCSI Target Mode Code device targbh #SCSI Target Mode Blackhole Device device pass #CAM passthrough driver # CAM OPTIONS: # debugging options: # -- NOTE -- If you specify one of the bus/target/lun options, you must # specify them all! # CAMDEBUG: When defined enables debugging macros # CAM_DEBUG_BUS: Debug the given bus. Use -1 to debug all busses. # CAM_DEBUG_TARGET: Debug the given target. Use -1 to debug all targets. # CAM_DEBUG_LUN: Debug the given lun. Use -1 to debug all luns. # CAM_DEBUG_FLAGS: OR together CAM_DEBUG_INFO, CAM_DEBUG_TRACE, # CAM_DEBUG_SUBTRACE, and CAM_DEBUG_CDB # # CAM_MAX_HIGHPOWER: Maximum number of concurrent high power (start unit) cmds # CAM_NEW_TRAN_CODE: this is the new transport layer code that will be switched # to soon # SCSI_NO_SENSE_STRINGS: When defined disables sense descriptions # SCSI_NO_OP_STRINGS: When defined disables opcode descriptions # SCSI_DELAY: The number of MILLISECONDS to freeze the SIM (scsi adapter) # queue after a bus reset, and the number of milliseconds to # freeze the device queue after a bus device reset. This # can be changed at boot and runtime with the # kern.cam.scsi_delay tunable/sysctl. options CAMDEBUG options CAM_DEBUG_BUS=-1 options CAM_DEBUG_TARGET=-1 options CAM_DEBUG_LUN=-1 options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB) options CAM_MAX_HIGHPOWER=4 options SCSI_NO_SENSE_STRINGS options SCSI_NO_OP_STRINGS options SCSI_DELAY=8000 # Be pessimistic about Joe SCSI device # Options for the CAM CDROM driver: # CHANGER_MIN_BUSY_SECONDS: Guaranteed minimum time quantum for a changer LUN # CHANGER_MAX_BUSY_SECONDS: Maximum time quantum per changer LUN, only # enforced if there is I/O waiting for another LUN # The compiled in defaults for these variables are 2 and 10 seconds, # respectively. # # These can also be changed on the fly with the following sysctl variables: # kern.cam.cd.changer.min_busy_seconds # kern.cam.cd.changer.max_busy_seconds # options CHANGER_MIN_BUSY_SECONDS=2 options CHANGER_MAX_BUSY_SECONDS=10 # Options for the CAM sequential access driver: # SA_IO_TIMEOUT: Timeout for read/write/wfm operations, in minutes # SA_SPACE_TIMEOUT: Timeout for space operations, in minutes # SA_REWIND_TIMEOUT: Timeout for rewind operations, in minutes # SA_ERASE_TIMEOUT: Timeout for erase operations, in minutes # SA_1FM_AT_EOD: Default to model which only has a default one filemark at EOT. options SA_IO_TIMEOUT=4 options SA_SPACE_TIMEOUT=60 options SA_REWIND_TIMEOUT=(2*60) options SA_ERASE_TIMEOUT=(4*60) options SA_1FM_AT_EOD # Optional timeout for the CAM processor target (pt) device # This is specified in seconds. The default is 60 seconds. options SCSI_PT_DEFAULT_TIMEOUT=60 # Optional enable of doing SES passthrough on other devices (e.g., disks) # # Normally disabled because a lot of newer SCSI disks report themselves # as having SES capabilities, but this can then clot up attempts to build # build a topology with the SES device that's on the box these drives # are in.... options SES_ENABLE_PASSTHROUGH ##################################################################### # MISCELLANEOUS DEVICES AND OPTIONS # The `pty' device usually turns out to be ``effectively mandatory'', # as it is required for `telnetd', `rlogind', `screen', `emacs', and # `xterm', among others. device pty #Pseudo ttys device nmdm #back-to-back tty devices device md #Memory/malloc disk device snp #Snoop device - to look at pty/vty/etc.. device ccd #Concatenated disk driver # Configuring Vinum into the kernel is not necessary, since the kld # module gets started automatically when vinum(8) starts. This # device is also untested. Use at your own risk. # # The option VINUMDEBUG must match the value set in CFLAGS # in src/sbin/vinum/Makefile. Failure to do so will result in # the following message from vinum(8): # # Can't get vinum config: Invalid argument # # see vinum(4) for more reasons not to use these options. device vinum #Vinum concat/mirror/raid driver options VINUMDEBUG #enable Vinum debugging hooks # Kernel side iconv library options LIBICONV # Size of the kernel message buffer. Should be N * pagesize. options MSGBUF_SIZE=40960 # Maximum size of a tty or pty input buffer. options TTYHOG=8193 ##################################################################### # HARDWARE DEVICE CONFIGURATION # For ISA the required hints are listed. # EISA, MCA, PCI and pccard are self identifying buses, so no hints # are needed. # # Mandatory devices: # # The keyboard controller; it controls the keyboard and the PS/2 mouse. device atkbdc hint.atkbdc.0.at="isa" hint.atkbdc.0.port="0x060" # The AT keyboard device atkbd hint.atkbd.0.at="atkbdc" hint.atkbd.0.irq="1" # Options for atkbd: options ATKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions ATKBD_DFLT_KEYMAP=jp.106 # These options are valid for other keyboard drivers as well. options KBD_DISABLE_KEYMAP_LOAD # refuse to load a keymap options KBD_INSTALL_CDEV # install a CDEV entry in /dev # `flags' for atkbd: # 0x01 Force detection of keyboard, else we always assume a keyboard # 0x02 Don't reset keyboard, useful for some newer ThinkPads # 0x03 Force detection and avoid reset, might help with certain # dockingstations # 0x04 Old-style (XT) keyboard support, useful for older ThinkPads # PS/2 mouse device psm hint.psm.0.at="atkbdc" hint.psm.0.irq="12" # Options for psm: options PSM_HOOKRESUME #hook the system resume event, useful #for some laptops options PSM_RESETAFTERSUSPEND #reset the device at the resume event # Video card driver for VGA adapters. device vga hint.vga.0.at="isa" # Options for vga: # Try the following option if the mouse pointer is not drawn correctly # or font does not seem to be loaded properly. May cause flicker on # some systems. options VGA_ALT_SEQACCESS # If you can dispense with some vga driver features, you may want to # use the following options to save some memory. #options VGA_NO_FONT_LOADING # don't save/load font #options VGA_NO_MODE_CHANGE # don't change video modes # Older video cards may require this option for proper operation. options VGA_SLOW_IOACCESS # do byte-wide i/o's to TS and GDC regs # The following option probably won't work with the LCD displays. options VGA_WIDTH90 # support 90 column modes options FB_DEBUG # Frame buffer debugging device splash # Splash screen and screen saver support # Various screen savers. device blank_saver device daemon_saver device fade_saver device fire_saver device green_saver device logo_saver device rain_saver device star_saver device warp_saver # The syscons console driver (sco color console compatible). device sc hint.sc.0.at="isa" options MAXCONS=16 # number of virtual consoles options SC_ALT_MOUSE_IMAGE # simplified mouse cursor in text mode options SC_DFLT_FONT # compile font in makeoptions SC_DFLT_FONT=cp850 options SC_DISABLE_DDBKEY # disable `debug' key options SC_DISABLE_REBOOT # disable reboot key sequence options SC_HISTORY_SIZE=200 # number of history buffer lines options SC_MOUSE_CHAR=0x3 # char code for text mode mouse cursor options SC_PIXEL_MODE # add support for the raster text mode # The following options will let you change the default colors of syscons. options SC_NORM_ATTR=(FG_GREEN|BG_BLACK) options SC_NORM_REV_ATTR=(FG_YELLOW|BG_GREEN) options SC_KERNEL_CONS_ATTR=(FG_RED|BG_BLACK) options SC_KERNEL_CONS_REV_ATTR=(FG_BLACK|BG_RED) # The following options will let you change the default behaviour of # cut-n-paste feature options SC_CUT_SPACES2TABS # convert leading spaces into tabs options SC_CUT_SEPCHARS=\"x09\" # set of characters that delimit words # (default is single space - \"x20\") # If you have a two button mouse, you may want to add the following option # to use the right button of the mouse to paste text. options SC_TWOBUTTON_MOUSE # You can selectively disable features in syscons. options SC_NO_CUTPASTE options SC_NO_FONT_LOADING options SC_NO_HISTORY options SC_NO_SYSMOUSE options SC_NO_SUSPEND_VTYSWITCH # `flags' for sc # 0x80 Put the video card in the VESA 800x600 dots, 16 color mode # 0x100 Probe for a keyboard device periodically if one is not present # # Optional devices: # # # SCSI host adapters: # # adv: All Narrow SCSI bus AdvanSys controllers. # adw: Second Generation AdvanSys controllers including the ADV940UW. # aha: Adaptec 154x/1535/1640 # ahb: Adaptec 174x EISA controllers # ahc: Adaptec 274x/284x/2910/293x/294x/394x/3950x/3960x/398X/4944/ # 19160x/29160x, aic7770/aic78xx # ahd: Adaptec 29320/39320 Controllers. # aic: Adaptec 6260/6360, APA-1460 (PC Card), NEC PC9801-100 (C-BUS) # amd: Support for the AMD 53C974 SCSI host adapter chip as found on devices # such as the Tekram DC-390(T). # bt: Most Buslogic controllers: including BT-445, BT-54x, BT-64x, BT-74x, # BT-75x, BT-946, BT-948, BT-956, BT-958, SDC3211B, SDC3211F, SDC3222F # esp: NCR53c9x. Only for SBUS hardware right now. # isp: Qlogic ISP 1020, 1040 and 1040B PCI SCSI host adapters, # ISP 1240 Dual Ultra SCSI, ISP 1080 and 1280 (Dual) Ultra2, # ISP 12160 Ultra3 SCSI, # Qlogic ISP 2100 and ISP 2200 1Gb Fibre Channel host adapters. # Qlogic ISP 2300 and ISP 2312 2Gb Fibre Channel host adapters. # ispfw: Firmware module for Qlogic host adapters # mpt: LSI-Logic MPT/Fusion 53c1020 or 53c1030 Ultra4 # or FC9x9 Fibre Channel host adapters. # ncr: NCR 53C810, 53C825 self-contained SCSI host adapters. # sym: Symbios/Logic 53C8XX family of PCI-SCSI I/O processors: # 53C810, 53C810A, 53C815, 53C825, 53C825A, 53C860, 53C875, # 53C876, 53C885, 53C895, 53C895A, 53C896, 53C897, 53C1510D, # 53C1010-33, 53C1010-66. # trm: Tekram DC395U/UW/F DC315U adapters. # wds: WD7000 # # Note that the order is important in order for Buslogic ISA/EISA cards to be # probed correctly. # device bt hint.bt.0.at="isa" hint.bt.0.port="0x330" device adv hint.adv.0.at="isa" device adw device aha hint.aha.0.at="isa" device aic hint.aic.0.at="isa" device ahb device ahc device ahd device amd device esp device isp hint.isp.0.disable="1" hint.isp.0.role="3" hint.isp.0.prefer_iomap="1" hint.isp.0.prefer_memmap="1" hint.isp.0.fwload_disable="1" hint.isp.0.ignore_nvram="1" hint.isp.0.fullduplex="1" hint.isp.0.topology="lport" hint.isp.0.topology="nport" hint.isp.0.topology="lport-only" hint.isp.0.topology="nport-only" # we can't get u_int64_t types, nor can we get strings if it's got # a leading 0x, hence this silly dodge. hint.isp.0.portwnn="w50000000aaaa0000" hint.isp.0.nodewnn="w50000000aaaa0001" device ispfw device mpt device ncr device sym device trm device wds hint.wds.0.at="isa" hint.wds.0.port="0x350" hint.wds.0.irq="11" hint.wds.0.drq="6" # The aic7xxx driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. Unfortunately, # this doesn't work on some motherboards, which prevents it from being the # default. options AHC_ALLOW_MEMIO # Dump the contents of the ahc controller configuration PROM. options AHC_DUMP_EEPROM # Bitmap of units to enable targetmode operations. options AHC_TMODE_ENABLE # Compile in Aic7xxx Debugging code. options AHC_DEBUG # Aic7xxx driver debugging options. See sys/dev/aic7xxx/aic7xxx.h options AHC_DEBUG_OPTS # Print register bitfields in debug output. Adds ~128k to driver # See ahc(4). options AHC_REG_PRETTY_PRINT # Compile in aic79xx debugging code. options AHD_DEBUG # Aic79xx driver debugging options. Adds ~215k to driver. See ahd(4). options AHD_DEBUG_OPTS=0xFFFFFFFF # Print human-readable register definitions when debugging options AHD_REG_PRETTY_PRINT # Bitmap of units to enable targetmode operations. options AHD_TMODE_ENABLE # The adw driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. options ADW_ALLOW_MEMIO # Options used in dev/isp/ (Qlogic SCSI/FC driver). # # ISP_TARGET_MODE - enable target mode operation # options ISP_TARGET_MODE=1 # Options used in dev/sym/ (Symbios SCSI driver). #options SYM_SETUP_LP_PROBE_MAP #-Low Priority Probe Map (bits) # Allows the ncr to take precedence # 1 (1<<0) -> 810a, 860 # 2 (1<<1) -> 825a, 875, 885, 895 # 4 (1<<2) -> 895a, 896, 1510d #options SYM_SETUP_SCSI_DIFF #-HVD support for 825a, 875, 885 # disabled:0 (default), enabled:1 #options SYM_SETUP_PCI_PARITY #-PCI parity checking # disabled:0, enabled:1 (default) #options SYM_SETUP_MAX_LUN #-Number of LUNs supported # default:8, range:[1..64] # The 'asr' driver provides support for current DPT/Adaptec SCSI RAID # controllers (SmartRAID V and VI and later). # These controllers require the CAM infrastructure. # device asr # The 'dpt' driver provides support for old DPT controllers (http://www.dpt.com/). # These have hardware RAID-{0,1,5} support, and do multi-initiator I/O. # The DPT controllers are commonly re-licensed under other brand-names - # some controllers by Olivetti, Dec, HP, AT&T, SNI, AST, Alphatronic, NEC and # Compaq are actually DPT controllers. # # See src/sys/dev/dpt for debugging and other subtle options. # DPT_MEASURE_PERFORMANCE Enables a set of (semi)invasive metrics. Various # instruments are enabled. The tools in # /usr/sbin/dpt_* assume these to be enabled. # DPT_HANDLE_TIMEOUTS Normally device timeouts are handled by the DPT. # If you ant the driver to handle timeouts, enable # this option. If your system is very busy, this # option will create more trouble than solve. # DPT_TIMEOUT_FACTOR Used to compute the excessive amount of time to # wait when timing out with the above option. # DPT_DEBUG_xxxx These are controllable from sys/dev/dpt/dpt.h # DPT_LOST_IRQ When enabled, will try, once per second, to catch # any interrupt that got lost. Seems to help in some # DPT-firmware/Motherboard combinations. Minimal # cost, great benefit. # DPT_RESET_HBA Make "reset" actually reset the controller # instead of fudging it. Only enable this if you # are 100% certain you need it. device dpt # DPT options #!CAM# options DPT_MEASURE_PERFORMANCE #!CAM# options DPT_HANDLE_TIMEOUTS options DPT_TIMEOUT_FACTOR=4 options DPT_LOST_IRQ options DPT_RESET_HBA # # Compaq "CISS" RAID controllers (SmartRAID 5* series) # These controllers have a SCSI-like interface, and require the # CAM infrastructure. # device ciss # # Intel Integrated RAID controllers. # This driver was developed and is maintained by Intel. Contacts # at Intel for this driver are # "Kannanthanam, Boji T" and # "Leubner, Achim" . # device iir # # Mylex AcceleRAID and eXtremeRAID controllers with v6 and later # firmware. These controllers have a SCSI-like interface, and require # the CAM infrastructure. # device mly # # Compaq Smart RAID, Mylex DAC960 and AMI MegaRAID controllers. Only # one entry is needed; the code will find and configure all supported # controllers. # device ida # Compaq Smart RAID device mlx # Mylex DAC960 device amr # AMI MegaRAID # # 3ware ATA RAID # device twe # 3ware ATA RAID # # The 'ATA' driver supports all ATA and ATAPI devices, including PC Card # devices. You only need one "device ata" for it to find all # PCI and PC Card ATA/ATAPI devices on modern machines. device ata device atadisk # ATA disk drives device ataraid # ATA RAID drives device atapicd # ATAPI CDROM drives device atapifd # ATAPI floppy drives device atapist # ATAPI tape drives device atapicam # emulate ATAPI devices as SCSI ditto via CAM # needs CAM to be present (scbus & pass) # # For older non-PCI, non-PnPBIOS systems, these are the hints lines to add: hint.ata.0.at="isa" hint.ata.0.port="0x1f0" hint.ata.0.irq="14" hint.ata.1.at="isa" hint.ata.1.port="0x170" hint.ata.1.irq="15" # # The following options are valid on the ATA driver: # # ATA_STATIC_ID: controller numbering is static ie depends on location # else the device numbers are dynamically allocated. options ATA_STATIC_ID # # Standard floppy disk controllers and floppy tapes, supports # the Y-E DATA External FDD (PC Card) # device fdc hint.fdc.0.at="isa" hint.fdc.0.port="0x3F0" hint.fdc.0.irq="6" hint.fdc.0.drq="2" # # FDC_DEBUG enables floppy debugging. Since the debug output is huge, you # gotta turn it actually on by setting the variable fd_debug with DDB, # however. options FDC_DEBUG # # Activate this line if you happen to have an Insight floppy tape. # Probing them proved to be dangerous for people with floppy disks only, # so it's "hidden" behind a flag: #hint.fdc.0.flags="1" # Specify floppy devices hint.fd.0.at="fdc0" hint.fd.0.drive="0" hint.fd.1.at="fdc0" hint.fd.1.drive="1" # # sio: serial ports (see sio(4)), including support for various # PC Card devices, such as Modem and NICs (see etc/defaults/pccard.conf) # device sio hint.sio.0.at="isa" hint.sio.0.port="0x3F8" hint.sio.0.flags="0x10" hint.sio.0.irq="4" # Options for sio: options COM_ESP # Code for Hayes ESP. options COM_MULTIPORT # Code for some cards with shared IRQs. options CONSPEED=115200 # Speed for serial console # (default 9600). # `flags' specific to sio(4). See below for flags used by both sio(4) and # uart(4). # 0x20 force this unit to be the console (unless there is another # higher priority console). This replaces the COMCONSOLE option. # 0x40 reserve this unit for low level console operations. Do not # access the device in any normal way. # PnP `flags' # 0x1 disable probing of this device. Used to prevent your modem # from being attached as a PnP modem. # Other flags for sio that aren't documented in the man page. # 0x20000 enable hardware RTS/CTS and larger FIFOs. Only works for # ST16650A-compatible UARTs. # # uart: newbusified driver for serial interfaces. It consolidates the sio(4), # sab(4) and zs(4) drivers. # device uart # Options for uart(4) options UART_PPS_ON_CTS # Do time pulse capturing using CTS # instead of DCD. # The following hint should only be used for pure ISA devices. It is not # needed otherwise. Use of hints is strongly discouraged. hint.uart.0.at="isa" # The following 3 hints are used when the UART is a system device (i.e., a # console or debug port), but only on platforms that don't have any other # means to pass the information to the kernel. The unit number of the hint # is only used to bundle the hints together. There is no relation to the # unit number of the probed UART. hint.uart.0.port="0x3f8" hint.uart.0.flags="0x10" hint.uart.0.baud="115200" # `flags' for serial drivers that support consoles like sio(4) and uart(4): # 0x10 enable console support for this unit. Other console flags # (if applicable) are ignored unless this is set. Enabling # console support does not make the unit the preferred console. # Boot with -h or set boot_serial=YES in the loader. For sio(4) # specifically, the 0x20 flag can also be set (see above). # Currently, at most one unit can have console support; the # first one (in config file order) with this flag set is # preferred. Setting this flag for sio0 gives the old behaviour. # 0x80 use this port for serial line gdb support in ddb. Also known # as debug port. # # Options for serial drivers that support consoles: options BREAK_TO_DEBUGGER # A BREAK on a serial console goes to # ddb, if available. # Solaris implements a new BREAK which is initiated by a character # sequence CR ~ ^b which is similar to a familiar pattern used on # Sun servers by the Remote Console. options ALT_BREAK_TO_DEBUGGER # PCI Universal Communications driver # Supports various single and multi port PCI serial cards. Maybe later # also the parallel ports on combination serial/parallel cards. New cards # can be added in src/sys/dev/puc/pucdata.c. # # If the PUC_FASTINTR option is used the driver will try to use fast # interrupts. The card must then be the only user of that interrupt. # Interrupts cannot be shared when using PUC_FASTINTR. device puc options PUC_FASTINTR # # Network interfaces: # # MII bus support is required for some PCI 10/100 ethernet NICs, # namely those which use MII-compliant transceivers or implement # tranceiver control interfaces that operate like an MII. Adding # "device miibus0" to the kernel config pulls in support for # the generic miibus API and all of the PHY drivers, including a # generic one for PHYs that aren't specifically handled by an # individual driver. device miibus # an: Aironet 4500/4800 802.11 wireless adapters. Supports the PCMCIA, # PCI and ISA varieties. # awi: Support for IEEE 802.11 PC Card devices using the AMD Am79C930 and # Harris (Intersil) Chipset with PCnetMobile firmware by AMD. # bge: Support for gigabit ethernet adapters based on the Broadcom # BCM570x family of controllers, including the 3Com 3c996-T, # the Netgear GA302T, the SysKonnect SK-9D21 and SK-9D41, and # the embedded gigE NICs on Dell PowerEdge 2550 servers. # cm: Arcnet SMC COM90c26 / SMC COM90c56 # (and SMC COM90c66 in '56 compatibility mode) adapters. # cnw: Xircom CNW/Netware Airsurfer PC Card adapter # cs: IBM Etherjet and other Crystal Semi CS89x0-based adapters # dc: Support for PCI fast ethernet adapters based on the DEC/Intel 21143 # and various workalikes including: # the ADMtek AL981 Comet and AN985 Centaur, the ASIX Electronics # AX88140A and AX88141, the Davicom DM9100 and DM9102, the Lite-On # 82c168 and 82c169 PNIC, the Lite-On/Macronix LC82C115 PNIC II # and the Macronix 98713/98713A/98715/98715A/98725 PMAC. This driver # replaces the old al, ax, dm, pn and mx drivers. List of brands: # Digital DE500-BA, Kingston KNE100TX, D-Link DFE-570TX, SOHOware SFA110, # SVEC PN102-TX, CNet Pro110B, 120A, and 120B, Compex RL100-TX, # LinkSys LNE100TX, LNE100TX V2.0, Jaton XpressNet, Alfa Inc GFC2204, # KNE110TX. # de: Digital Equipment DC21040 # em: Intel Pro/1000 Gigabit Ethernet 82542, 82543, 82544 based adapters. # ep: 3Com 3C509, 3C529, 3C556, 3C562D, 3C563D, 3C572, 3C574X, 3C579, 3C589 # and PC Card devices using these chipsets. # ex: Intel EtherExpress Pro/10 and other i82595-based adapters, # Olicom Ethernet PC Card devices. # fe: Fujitsu MB86960A/MB86965A Ethernet # fea: DEC DEFEA EISA FDDI adapter # fpa: Support for the Digital DEFPA PCI FDDI. `device fddi' is also needed. # fxp: Intel EtherExpress Pro/100B # (hint of prefer_iomap can be done to prefer I/O instead of Mem mapping) # gx: Intel Pro/1000 Gigabit Ethernet (82542, 82543-F, 82543-T) # lge: Support for PCI gigabit ethernet adapters based on the Level 1 # LXT1001 NetCellerator chipset. This includes the D-Link DGE-500SX, # SMC TigerCard 1000 (SMC9462SX), and some Addtron cards. # my: Myson Fast Ethernet (MTD80X, MTD89X) # nge: Support for PCI gigabit ethernet adapters based on the National # Semiconductor DP83820 and DP83821 chipset. This includes the # SMC EZ Card 1000 (SMC9462TX), D-Link DGE-500T, Asante FriendlyNet # GigaNIX 1000TA and 1000TPC, the Addtron AEG320T, the LinkSys # EG1032 and EG1064, the Surecom EP-320G-TX and the Netgear GA622T. # pcn: Support for PCI fast ethernet adapters based on the AMD Am79c97x # chipsets, including the PCnet/FAST, PCnet/FAST+, PCnet/PRO and # PCnet/Home. These were previously handled by the lnc driver (and # still will be if you leave this driver out of the kernel). # rl: Support for PCI fast ethernet adapters based on the RealTek 8129/8139 # chipset. Note that the RealTek driver defaults to using programmed # I/O to do register accesses because memory mapped mode seems to cause # severe lockups on SMP hardware. This driver also supports the # Accton EN1207D `Cheetah' adapter, which uses a chip called # the MPX 5030/5038, which is either a RealTek in disguise or a # RealTek workalike. Note that the D-Link DFE-530TX+ uses the RealTek # chipset and is supported by this driver, not the 'vr' driver. # sf: Support for Adaptec Duralink PCI fast ethernet adapters based on the # Adaptec AIC-6915 "starfire" controller. # This includes dual and quad port cards, as well as one 100baseFX card. # Most of these are 64-bit PCI devices, except for one single port # card which is 32-bit. # sis: Support for NICs based on the Silicon Integrated Systems SiS 900, # SiS 7016 and NS DP83815 PCI fast ethernet controller chips. # sbsh: Support for Granch SBNI16 SHDSL modem PCI adapters # sk: Support for the SysKonnect SK-984x series PCI gigabit ethernet NICs. # This includes the SK-9841 and SK-9842 single port cards (single mode # and multimode fiber) and the SK-9843 and SK-9844 dual port cards # (also single mode and multimode). # The driver will autodetect the number of ports on the card and # attach each one as a separate network interface. # sn: Support for ISA and PC Card Ethernet devices using the # SMC91C90/92/94/95 chips. # ste: Sundance Technologies ST201 PCI fast ethernet controller, includes # the D-Link DFE-550TX. # ti: Support for PCI gigabit ethernet NICs based on the Alteon Networks # Tigon 1 and Tigon 2 chipsets. This includes the Alteon AceNIC, the # 3Com 3c985, the Netgear GA620 and various others. Note that you will # probably want to bump up NMBCLUSTERS a lot to use this driver. # tl: Support for the Texas Instruments TNETE100 series 'ThunderLAN' # cards and integrated ethernet controllers. This includes several # Compaq Netelligent 10/100 cards and the built-in ethernet controllers # in several Compaq Prosignia, Proliant and Deskpro systems. It also # supports several Olicom 10Mbps and 10/100 boards. # tx: SMC 9432 TX, BTX and FTX cards. (SMC EtherPower II serie) # txp: Support for 3Com 3cR990 cards with the "Typhoon" chipset # vr: Support for various fast ethernet adapters based on the VIA # Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' chips, # including the D-Link DFE530TX (see 'rl' for DFE530TX+), the Hawking # Technologies PN102TX, and the AOpen/Acer ALN-320. # vx: 3Com 3C590 and 3C595 # wb: Support for fast ethernet adapters based on the Winbond W89C840F chip. # Note: this is not the same as the Winbond W89C940F, which is a # NE2000 clone. # wi: Lucent WaveLAN/IEEE 802.11 PCMCIA adapters. Note: this supports both # the PCMCIA and ISA cards: the ISA card is really a PCMCIA to ISA # bridge with a PCMCIA adapter plugged into it. # xe: Xircom/Intel EtherExpress Pro100/16 PC Card ethernet controller, # Accton Fast EtherCard-16, Compaq Netelligent 10/100 PC Card, # Toshiba 10/100 Ethernet PC Card, Xircom 16-bit Ethernet + Modem 56 # xl: Support for the 3Com 3c900, 3c905, 3c905B and 3c905C (Fast) # Etherlink XL cards and integrated controllers. This includes the # integrated 3c905B-TX chips in certain Dell Optiplex and Dell # Precision desktop machines and the integrated 3c905-TX chips # in Dell Latitude laptop docking stations. # Also supported: 3Com 3c980(C)-TX, 3Com 3cSOHO100-TX, 3Com 3c450-TX # Order for ISA/EISA devices is important here device cm hint.cm.0.at="isa" hint.cm.0.port="0x2e0" hint.cm.0.irq="9" hint.cm.0.maddr="0xdc000" device cs hint.cs.0.at="isa" hint.cs.0.port="0x300" device ep device ex device fe hint.fe.0.at="isa" hint.fe.0.port="0x300" device fea device sn hint.sn.0.at="isa" hint.sn.0.port="0x300" hint.sn.0.irq="10" device an device awi device cnw device wi device xe # PCI Ethernet NICs that use the common MII bus controller code. device dc # DEC/Intel 21143 and various workalikes device fxp # Intel EtherExpress PRO/100B (82557, 82558) hint.fxp.0.prefer_iomap="0" device my # Myson Fast Ethernet (MTD80X, MTD89X) device rl # RealTek 8129/8139 device pcn # AMD Am79C97x PCI 10/100 NICs device sf # Adaptec AIC-6915 (``Starfire'') device sbsh # Granch SBNI16 SHDSL modem device sis # Silicon Integrated Systems SiS 900/SiS 7016 device ste # Sundance ST201 (D-Link DFE-550TX) device tl # Texas Instruments ThunderLAN device tx # SMC EtherPower II (83c170 ``EPIC'') device vr # VIA Rhine, Rhine II device wb # Winbond W89C840F device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'') # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') device txp # 3Com 3cR990 (``Typhoon'') device vx # 3Com 3c590, 3c595 (``Vortex'') # PCI Gigabit & FDDI NICs. device bge device gx device lge device nge device sk device ti device fpa # Use "private" jumbo buffers allocated exclusively for the ti(4) driver. # This option is incompatible with the TI_JUMBO_HDRSPLIT option below. #options TI_PRIVATE_JUMBOS # Turn on the header splitting option for the ti(4) driver firmware. This # only works for Tigon II chips, and has no effect for Tigon I chips. options TI_JUMBO_HDRSPLIT # These two options allow manipulating the mbuf cluster size and mbuf size, # respectively. Be very careful with NIC driver modules when changing # these from their default values, because that can potentially cause a # mismatch between the mbuf size assumed by the kernel and the mbuf size # assumed by a module. The only driver that currently has the ability to # detect a mismatch is ti(4). options MCLSHIFT=12 # mbuf cluster shift in bits, 12 == 4KB options MSIZE=512 # mbuf size in bytes # # ATM related options (Cranor version) # (note: this driver cannot be used with the HARP ATM stack) # # The `en' device provides support for Efficient Networks (ENI) # ENI-155 PCI midway cards, and the Adaptec 155Mbps PCI ATM cards (ANA-59x0). # # The `hatm' device provides support for Fore/Marconi HE155 and HE622 # ATM PCI cards. # # The `fatm' device provides support for Fore PCA200E ATM PCI cards. # # The `patm' device provides support for IDT77252 based cards like # ProSum's ProATM-155 and ProATM-25 and IDT's evaluation boards. # # atm device provides generic atm functions and is required for # atm devices. # NATM enables the netnatm protocol family that can be used to # bypass TCP/IP. # # utopia provides the access to the ATM PHY chips and is required for en, # hatm and fatm. # # the current driver supports only PVC operations (no atm-arp, no multicast). # for more details, please read the original documents at # http://www.ccrc.wustl.edu/pub/chuck/tech/bsdatm/bsdatm.html # device atm device en device fatm #Fore PCA200E device hatm #Fore/Marconi HE155/622 device patm #IDT77252 cards (ProATM and IDT) device utopia #ATM PHY driver options NATM #native ATM options LIBMBPOOL #needed by patm, iatm # # Audio drivers: `pcm', `sbc', `gusc' # # pcm: PCM audio through various sound cards. # # This has support for a large number of new audio cards, based on # CS423x, OPTi931, Yamaha OPL-SAx, and also for SB16, GusPnP. # For more information about this driver and supported cards, see pcm(4). # # The flags of the device tells the device a bit more info about the # device that normally is obtained through the PnP interface. # bit 2..0 secondary DMA channel; # bit 4 set if the board uses two dma channels; # bit 15..8 board type, overrides autodetection; leave it # zero if don't know what to put in (and you don't, # since this is unsupported at the moment...). # # Supported cards include: # Creative SoundBlaster ISA PnP/non-PnP # Supports ESS and Avance ISA chips as well. # Gravis UltraSound ISA PnP/non-PnP # Crystal Semiconductor CS461x/428x PCI # Neomagic 256AV (ac97) # Most of the more common ISA/PnP sb/mss/ess compatable cards. device pcm # For non-pnp sound cards with no bridge drivers only: hint.pcm.0.at="isa" hint.pcm.0.irq="10" hint.pcm.0.drq="1" hint.pcm.0.flags="0x0" # The bridge drivers for sound cards. These can be separately configured # for providing services to the likes of new-midi. # When used with 'device pcm' they also provide pcm sound services. # # sbc: Creative SoundBlaster ISA PnP/non-PnP # Supports ESS and Avance ISA chips as well. # gusc: Gravis UltraSound ISA PnP/non-PnP # csa: Crystal Semiconductor CS461x/428x PCI # For non-PnP cards: device sbc hint.sbc.0.at="isa" hint.sbc.0.port="0x220" hint.sbc.0.irq="5" hint.sbc.0.drq="1" hint.sbc.0.flags="0x15" device gusc hint.gusc.0.at="isa" hint.gusc.0.port="0x220" hint.gusc.0.irq="5" hint.gusc.0.drq="1" hint.gusc.0.flags="0x13" # # Miscellaneous hardware: # # scd: Sony CD-ROM using proprietary (non-ATAPI) interface # mcd: Mitsumi CD-ROM using proprietary (non-ATAPI) interface # bktr: Brooktree bt848/848a/849a/878/879 video capture and TV Tuner board # cy: Cyclades serial driver # joy: joystick (including IO DATA PCJOY PC Card joystick) # rc: RISCom/8 multiport card # rp: Comtrol Rocketport(ISA/PCI) - single card # si: Specialix SI/XIO 4-32 port terminal multiplexor # nmdm: nullmodem terminal driver (see nmdm(4)) # Notes on the Comtrol Rocketport driver: # # The exact values used for rp0 depend on how many boards you have # in the system. The manufacturer's sample configs are listed as: # # device rp # core driver support # # Comtrol Rocketport ISA single card # hint.rp.0.at="isa" # hint.rp.0.port="0x280" # # If instead you have two ISA cards, one installed at 0x100 and the # second installed at 0x180, then you should add the following to # your kernel probe hints: # hint.rp.0.at="isa" # hint.rp.0.port="0x100" # hint.rp.1.at="isa" # hint.rp.1.port="0x180" # # For 4 ISA cards, it might be something like this: # hint.rp.0.at="isa" # hint.rp.0.port="0x180" # hint.rp.1.at="isa" # hint.rp.1.port="0x100" # hint.rp.2.at="isa" # hint.rp.2.port="0x340" # hint.rp.3.at="isa" # hint.rp.3.port="0x240" # # For PCI cards, you need no hints. # Mitsumi CD-ROM device mcd hint.mcd.0.at="isa" hint.mcd.0.port="0x300" # for the Sony CDU31/33A CDROM device scd hint.scd.0.at="isa" hint.scd.0.port="0x230" device joy # PnP aware, hints for nonpnp only hint.joy.0.at="isa" hint.joy.0.port="0x201" device rc hint.rc.0.at="isa" hint.rc.0.port="0x220" hint.rc.0.irq="12" device rp hint.rp.0.at="isa" hint.rp.0.port="0x280" device si options SI_DEBUG hint.si.0.at="isa" hint.si.0.maddr="0xd0000" hint.si.0.irq="12" device nmdm # # The 'bktr' device is a PCI video capture device using the Brooktree # bt848/bt848a/bt849a/bt878/bt879 chipset. When used with a TV Tuner it forms a # TV card, eg Miro PC/TV, Hauppauge WinCast/TV WinTV, VideoLogic Captivator, # Intel Smart Video III, AverMedia, IMS Turbo, FlyVideo. # # options OVERRIDE_CARD=xxx # options OVERRIDE_TUNER=xxx # options OVERRIDE_MSP=1 # options OVERRIDE_DBX=1 # These options can be used to override the auto detection # The current values for xxx are found in src/sys/dev/bktr/bktr_card.h # Using sysctl(8) run-time overrides on a per-card basis can be made # # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_PAL # or # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_NTSC # Specifes the default video capture mode. # This is required for Dual Crystal (28&35Mhz) boards where PAL is used # to prevent hangs during initialisation. eg VideoLogic Captivator PCI. # # options BKTR_USE_PLL # PAL or SECAM users who have a 28Mhz crystal (and no 35Mhz crystal) # must enable PLL mode with this option. eg some new Bt878 cards. # # options BKTR_GPIO_ACCESS # This enable IOCTLs which give user level access to the GPIO port. # # options BKTR_NO_MSP_RESET # Prevents the MSP34xx reset. Good if you initialise the MSP in another OS first # # options BKTR_430_FX_MODE # Switch Bt878/879 cards into Intel 430FX chipset compatibility mode. # # options BKTR_SIS_VIA_MODE # Switch Bt878/879 cards into SIS/VIA chipset compatibility mode which is # needed for some old SiS and VIA chipset motherboards. # This also allows Bt878/879 chips to work on old OPTi (<1997) chipset # motherboards and motherboards with bad or incomplete PCI 2.1 support. # As a rough guess, old = before 1998 # # options BKTR_NEW_MSP34XX_DRIVER # Use new, more complete initialization scheme for the msp34* soundchip. # Should fix stereo autodetection if the old driver does only output # mono sound. # # options BKTR_USE_FREEBSD_SMBUS # Compile with FreeBSD SMBus implementation # # Brooktree driver has been ported to the new I2C framework. Thus, # you'll need to have the following 3 lines in the kernel config. # device smbus # device iicbus # device iicbb # device iicsmb # The iic and smb devices are only needed if you want to control other # I2C slaves connected to the external connector of some cards. # device bktr # # PC Card/PCMCIA # (OLDCARD) # # card: pccard slots # pcic: isa/pccard bridge #device pcic #hint.pcic.0.at="isa" #hint.pcic.1.at="isa" #device card 1 # # PC Card/PCMCIA and Cardbus # (NEWCARD) # # Note that NEWCARD and OLDCARD are incompatible. Do not use both at the same # time. # # pccbb: pci/cardbus bridge implementing YENTA interface # pccard: pccard slots # cardbus: cardbus slots device cbb device pccard device cardbus #device pcic ISA attachment currently busted #hint.pcic.0.at="isa" #hint.pcic.1.at="isa" # # SMB bus # # System Management Bus support is provided by the 'smbus' device. # Access to the SMBus device is via the 'smb' device (/dev/smb*), # which is a child of the 'smbus' device. # # Supported devices: # smb standard io through /dev/smb* # # Supported SMB interfaces: # iicsmb I2C to SMB bridge with any iicbus interface # bktr brooktree848 I2C hardware interface # intpm Intel PIIX4 (82371AB, 82443MX) Power Management Unit # alpm Acer Aladdin-IV/V/Pro2 Power Management Unit # ichsmb Intel ICH SMBus controller chips (82801AA, 82801AB, 82801BA) # viapm VIA VT82C586B/596B/686A and VT8233 Power Management Unit # amdpm AMD 756 Power Management Unit # nfpm NVIDIA nForce Power Management Unit # device smbus # Bus support, required for smb below. device intpm device alpm device ichsmb device viapm device amdpm device nfpm device smb # # I2C Bus # # Philips i2c bus support is provided by the `iicbus' device. # # Supported devices: # ic i2c network interface # iic i2c standard io # iicsmb i2c to smb bridge. Allow i2c i/o with smb commands. # # Supported interfaces: # bktr brooktree848 I2C software interface # # Other: # iicbb generic I2C bit-banging code (needed by lpbb, bktr) # device iicbus # Bus support, required for ic/iic/iicsmb below. device iicbb device ic device iic device iicsmb # smb over i2c bridge # Parallel-Port Bus # # Parallel port bus support is provided by the `ppbus' device. # Multiple devices may be attached to the parallel port, devices # are automatically probed and attached when found. # # Supported devices: # vpo Iomega Zip Drive # Requires SCSI disk support ('scbus' and 'da'), best # performance is achieved with ports in EPP 1.9 mode. # lpt Parallel Printer # plip Parallel network interface # ppi General-purpose I/O ("Geek Port") + IEEE1284 I/O # pps Pulse per second Timing Interface # lpbb Philips official parallel port I2C bit-banging interface # # Supported interfaces: # ppc ISA-bus parallel port interfaces. # options PPC_PROBE_CHIPSET # Enable chipset specific detection # (see flags in ppc(4)) options DEBUG_1284 # IEEE1284 signaling protocol debug options PERIPH_1284 # Makes your computer act as an IEEE1284 # compliant peripheral options DONTPROBE_1284 # Avoid boot detection of PnP parallel devices options VP0_DEBUG # ZIP/ZIP+ debug options LPT_DEBUG # Printer driver debug options PPC_DEBUG # Parallel chipset level debug options PLIP_DEBUG # Parallel network IP interface debug options PCFCLOCK_VERBOSE # Verbose pcfclock driver options PCFCLOCK_MAX_RETRIES=5 # Maximum read tries (default 10) device ppc hint.ppc.0.at="isa" hint.ppc.0.irq="7" device ppbus device vpo device lpt device plip device ppi device pps device lpbb device pcfclock # Kernel BOOTP support options BOOTP # Use BOOTP to obtain IP address/hostname # Requires NFSCLIENT and NFS_ROOT options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info options BOOTP_NFSV3 # Use NFS v3 to NFS mount root options BOOTP_COMPAT # Workaround for broken bootp daemons. options BOOTP_WIRED_TO=fxp0 # Use interface fxp0 for BOOTP # # Add tie-ins for a hardware watchdog. This only enables the hooks; # the user must still supply the actual driver. # options HW_WDOG # # Add software watchdog routines. # options SW_WATCHDOG # # Disable swapping of upages and stack pages. This option removes all # code which actually performs swapping, so it's not possible to turn # it back on at run-time. # # This is sometimes usable for systems which don't have any swap space # (see also sysctls "vm.defer_swapspace_pageouts" and # "vm.disable_swapspace_pageouts") # #options NO_SWAPPING # Set the number of sf_bufs to allocate. sf_bufs are virtual buffers # for sendfile(2) that are used to map file VM pages, and normally # default to a quantity that is roughly 16*MAXUSERS+512. You would # typically want about 4 of these for each simultaneous file send. # options NSFBUFS=1024 # # Enable extra debugging code for locks. This stores the filename and # line of whatever acquired the lock in the lock itself, and change a # number of function calls to pass around the relevant data. This is # not at all useful unless you are debugging lock code. Also note # that it is likely to break e.g. fstat(1) unless you recompile your # userland with -DDEBUG_LOCKS as well. # options DEBUG_LOCKS ##################################################################### # USB support # UHCI controller device uhci # OHCI controller device ohci # EHCI controller device ehci # General USB code (mandatory for USB) device usb # # USB Double Bulk Pipe devices device udbp # USB Fm Radio device ufm # Generic USB device driver device ugen # Human Interface Device (anything with buttons and dials) device uhid # USB keyboard device ukbd # USB printer device ulpt # USB Iomega Zip 100 Drive (Requires scbus and da) device umass # USB support for Belkin F5U109 and Magic Control Technology serial adapters device umct # USB modem support device umodem # USB mouse device ums # Diamond Rio 500 Mp3 player device urio # USB scanners device uscanner # # USB serial support device ucom # USB support for Belkin F5U103 and compatible serial adapters device ubsa # USB support for BWCT console serial adapters device ubser # USB support for serial adapters based on the FT8U100AX and FT8U232AM device uftdi # USB support for Prolific PL-2303 serial adapters device uplcom # USB Visor and Palm devices device uvisor # USB serial support for DDI pocket's PHS device uvscom # # ADMtek USB ethernet. Supports the LinkSys USB100TX, # the Billionton USB100, the Melco LU-ATX, the D-Link DSB-650TX # and the SMC 2202USB. Also works with the ADMtek AN986 Pegasus # eval board. device aue # # CATC USB-EL1201A USB ethernet. Supports the CATC Netmate # and Netmate II, and the Belkin F5U111. device cue # # Kawasaki LSI ethernet. Supports the LinkSys USB10T, # Entrega USB-NET-E45, Peracom Ethernet Adapter, the # 3Com 3c19250, the ADS Technologies USB-10BT, the ATen UC10T, # the Netgear EA101, the D-Link DSB-650, the SMC 2102USB # and 2104USB, and the Corega USB-T. device kue # # RealTek RTL8150 USB to fast ethernet. Supports the Melco LUA-KTX # and the GREEN HOUSE GH-USB100B. device rue # # Davicom DM9601E USB to fast ethernet. Supports the Corega FEther USB-TXC. device udav # debugging options for the USB subsystem # options USB_DEBUG # options for ukbd: options UKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions UKBD_DFLT_KEYMAP=it.iso # options for uplcom: options UPLCOM_INTR_INTERVAL=100 # interrpt pipe interval # in milliseconds # options for uvscom: options UVSCOM_DEFAULT_OPKTSIZE=8 # default output packet size options UVSCOM_INTR_INTERVAL=100 # interrpt pipe interval # in milliseconds ##################################################################### # FireWire support device firewire # FireWire bus code device sbp # SCSI over Firewire (Requires scbus and da) device sbp_targ # SBP-2 Target mode (Requires scbus and targ) device fwe # Ethernet over FireWire (non-standard!) ##################################################################### # dcons support (Dumb Console Device) device dcons # dumb console driver device dcons_crom # FireWire attachment options DCONS_BUF_SIZE=16384 # buffer size options DCONS_POLL_HZ=100 # polling rate options DCONS_FORCE_CONSOLE=0 # force to be the primary console options DCONS_FORCE_GDB=1 # force to be the gdb device ##################################################################### # crypto subsystem # # This is a port of the openbsd crypto framework. Include this when # configuring FAST_IPSEC and when you have a h/w crypto device to accelerate # user applications that link to openssl. # # Drivers are ports from openbsd with some simple enhancements that have # been fed back to openbsd. device crypto # core crypto support device cryptodev # /dev/crypto for access to h/w device rndtest # FIPS 140-2 entropy tester device hifn # Hifn 7951, 7781, etc. options HIFN_DEBUG # enable debugging support: hw.hifn.debug options HIFN_RNDTEST # enable rndtest support device ubsec # Broadcom 5501, 5601, 58xx options UBSEC_DEBUG # enable debugging support: hw.ubsec.debug options UBSEC_RNDTEST # enable rndtest support ##################################################################### # # Embedded system options: # # An embedded system might want to run something other than init. options INIT_PATH=/sbin/init:/stand/sysinstall # Debug options options BUS_DEBUG # enable newbus debugging options DEBUG_VFS_LOCKS # enable vfs lock debugging options SOCKBUF_DEBUG # enable sockbuf last record/mb tail checking ##################################################################### # SYSV IPC KERNEL PARAMETERS # # Maximum number of entries in a semaphore map. options SEMMAP=31 # Maximum number of System V semaphores that can be used on the system at # one time. options SEMMNI=11 # Total number of semaphores system wide options SEMMNS=61 # Total number of undo structures in system options SEMMNU=31 # Maximum number of System V semaphores that can be used by a single process # at one time. options SEMMSL=61 # Maximum number of operations that can be outstanding on a single System V # semaphore at one time. options SEMOPM=101 # Maximum number of undo operations that can be outstanding on a single # System V semaphore at one time. options SEMUME=11 # Maximum number of shared memory pages system wide. options SHMALL=1025 # Maximum size, in bytes, of a single System V shared memory region. options SHMMAX=(SHMMAXPGS*PAGE_SIZE+1) options SHMMAXPGS=1025 # Minimum size, in bytes, of a single System V shared memory region. options SHMMIN=2 # Maximum number of shared memory regions that can be used on the system # at one time. options SHMMNI=33 # Maximum number of System V shared memory regions that can be attached to # a single process at one time. options SHMSEG=9 # Set the amount of time (in seconds) the system will wait before # rebooting automatically when a kernel panic occurs. If set to (-1), # the system will wait indefinitely until a key is pressed on the # console. options PANIC_REBOOT_WAIT_TIME=16 # Attempt to bypass the buffer cache and put data directly into the # userland buffer for read operation when O_DIRECT flag is set on the # file. Both offset and length of the read operation must be # multiples of the physical media sector size. # #options DIRECTIO # Specify a lower limit for the number of swap I/O buffers. They are # (among other things) used when bypassing the buffer cache due to # DIRECTIO kernel option enabled and O_DIRECT flag set on file. # #options NSWBUF_MIN=120 ##################################################################### # More undocumented options for linting. # Note that documenting these are not considered an affront. options CAM_DEBUG_DELAY # VFS cluster debugging. options CLUSTERDEBUG options DEBUG # Kernel filelock debugging. options LOCKF_DEBUG # System V compatible message queues # Please note that the values provided here are used to test kernel # building. The defaults in the sources provide almost the same numbers. # MSGSSZ must be a power of 2 between 8 and 1024. options MSGMNB=2049 # Max number of chars in queue options MSGMNI=41 # Max number of message queue identifiers options MSGSEG=2049 # Max number of message segments options MSGSSZ=16 # Size of a message segment options MSGTQL=41 # Max number of messages in system options NBUF=512 # Number of buffer headers options NMBCLUSTERS=1024 # Number of mbuf clusters options SCSI_NCR_DEBUG options SCSI_NCR_MAX_SYNC=10000 options SCSI_NCR_MAX_WIDE=1 options SCSI_NCR_MYADDR=7 options SC_DEBUG_LEVEL=5 # Syscons debug level options SC_RENDER_DEBUG # syscons rendering debugging options SHOW_BUSYBUFS # List buffers that prevent root unmount options SLIP_IFF_OPTS options VFS_BIO_DEBUG # VFS buffer I/O debugging options KSTACK_MAX_PAGES=32 # Maximum pages to give the kernel stack # Adaptec Array Controller driver options options AAC_DEBUG # Debugging levels: # 0 - quiet, only emit warnings # 1 - noisy, emit major function # points and things done # 2 - extremely noisy, emit trace # items in loops, etc. # Yet more undocumented options for linting. # BKTR_ALLOC_PAGES has no effect except to cause warnings, and # BROOKTREE_ALLOC_PAGES hasn't actually been superseded by it, since the # driver still mostly spells this option BROOKTREE_ALLOC_PAGES. ##options BKTR_ALLOC_PAGES=(217*4+1) options BROOKTREE_ALLOC_PAGES=(217*4+1) options MAXFILES=999 options NDEVFSINO=1025 options NDEVFSOVERFLOW=32769 # Yet more undocumented options for linting. options VGA_DEBUG Index: head/sys/conf/options =================================================================== --- head/sys/conf/options (revision 130343) +++ head/sys/conf/options (revision 130344) @@ -1,655 +1,654 @@ # $FreeBSD$ # # On the handling of kernel options # # All kernel options should be listed in NOTES, with suitable # descriptions. Negative options (options that make some code not # compile) should be commented out; LINT (generated from NOTES) should # compile as much code as possible. Try to structure option-using # code so that a single option only switch code on, or only switch # code off, to make it possible to have a full compile-test. If # necessary, you can check for COMPILING_LINT to get maximum code # coverage. # # All new options shall also be listed in either "conf/options" or # "conf/options.". Options that affect a single source-file # .[c|s] should be directed into "opt_.h", while options # that affect multiple files should either go in "opt_global.h" if # this is a kernel-wide option (used just about everywhere), or in # "opt_.h" if it affect only some files. # Note that the effect of listing only an option without a # header-file-name in conf/options (and cousins) is that the last # convention is followed. # # This handling scheme is not yet fully implemented. # # # Format of this file: # Option name filename # # If filename is missing, the default is # opt_.h AAC_DEBUG opt_aac.h AHC_ALLOW_MEMIO opt_aic7xxx.h AHC_TMODE_ENABLE opt_aic7xxx.h AHC_DUMP_EEPROM opt_aic7xxx.h AHC_DEBUG opt_aic7xxx.h AHC_DEBUG_OPTS opt_aic7xxx.h AHC_REG_PRETTY_PRINT opt_aic7xxx.h AHD_DEBUG opt_aic79xx.h AHD_DEBUG_OPTS opt_aic79xx.h AHD_TMODE_ENABLE opt_aic79xx.h AHD_REG_PRETTY_PRINT opt_aic79xx.h ADW_ALLOW_MEMIO opt_adw.h TWA_DEBUG opt_twa.h TWA_FLASH_FIRMWARE opt_twa.h # Miscellaneous options. ADAPTIVE_MUTEXES ALQ CODA_COMPAT_5 opt_coda.h COMPAT_43 opt_compat.h COMPAT_FREEBSD4 opt_compat.h -COMPAT_SUNOS opt_compat.h COMPILING_LINT opt_global.h CONSPEED opt_comconsole.h CY_PCI_FASTINTR DDB DDB_NOKLDSYM opt_ddb.h DDB_NUMSYM opt_ddb.h DDB_TRACE DDB_UNATTENDED DIRECTIO opt_directio.h GDB_REMOTE_CHAT opt_ddb.h GDBSPEED opt_ddb.h GEOM_AES opt_geom.h GEOM_APPLE opt_geom.h GEOM_BDE opt_geom.h GEOM_BSD opt_geom.h GEOM_CONCAT opt_geom.h GEOM_FOX opt_geom.h GEOM_GATE opt_geom.h GEOM_GPT opt_geom.h GEOM_MBR opt_geom.h GEOM_MIRROR opt_geom.h GEOM_NOP opt_geom.h GEOM_PC98 opt_geom.h GEOM_STRIPE opt_geom.h GEOM_SUNLABEL opt_geom.h GEOM_VOL opt_geom.h HW_WDOG KSTACK_MAX_PAGES KSTACK_PAGES KTRACE KTRACE_REQUEST_POOL opt_ktrace.h LIBICONV MAC MAC_ALWAYS_LABEL_MBUF opt_mac.h MAC_BIBA opt_dontuse.h MAC_BSDEXTENDED opt_dontuse.h MAC_DEBUG opt_mac.h MAC_IFOFF opt_dontuse.h MAC_LOMAC opt_dontuse.h MAC_MLS opt_dontuse.h MAC_NONE opt_dontuse.h MAC_PARTITION opt_dontuse.h MAC_PORTACL opt_dontuse.h MAC_SEEOTHERUIDS opt_dontuse.h MAC_STATIC opt_mac.h MAC_STUB opt_dontuse.h MAC_TEST opt_dontuse.h MD_ROOT opt_md.h MD_ROOT_SIZE opt_md.h MUTEX_WAKE_ALL NSWBUF_MIN opt_swap.h PANIC_REBOOT_WAIT_TIME opt_panic.h PPS_SYNC opt_ntp.h PUC_FASTINTR opt_puc.h QUOTA SCHED_4BSD opt_sched.h SCHED_ULE opt_sched.h SHOW_BUSYBUFS SPX_HACK SUIDDIR opt_suiddir.h MSGMNB opt_sysvipc.h MSGMNI opt_sysvipc.h MSGSEG opt_sysvipc.h MSGSSZ opt_sysvipc.h MSGTQL opt_sysvipc.h SEMMAP opt_sysvipc.h SEMMNI opt_sysvipc.h SEMMNS opt_sysvipc.h SEMMNU opt_sysvipc.h SEMMSL opt_sysvipc.h SEMOPM opt_sysvipc.h SEMUME opt_sysvipc.h SHMALL opt_sysvipc.h SHMMAX opt_sysvipc.h SHMMAXPGS opt_sysvipc.h SHMMIN opt_sysvipc.h SHMMNI opt_sysvipc.h SHMSEG opt_sysvipc.h SYSVMSG opt_sysvipc.h SYSVSEM opt_sysvipc.h SYSVSHM opt_sysvipc.h SW_WATCHDOG opt_watchdog.h TTYHOG opt_tty.h VFS_AIO WLCACHE opt_wavelan.h WLDEBUG opt_wavelan.h # POSIX kernel options P1003_1B_SEMAPHORES opt_posix.h _KPOSIX_PRIORITY_SCHEDULING opt_posix.h # Do we want the config file compiled into the kernel? INCLUDE_CONFIG_FILE opt_config.h # Options for static filesystems. These should only be used at config # time, since the corresponding lkms cannot work if there are any static # dependencies. Unusability is enforced by hiding the defines for the # options in a never-included header. CD9660 opt_dontuse.h CODA opt_dontuse.h EXT2FS opt_dontuse.h FDESCFS opt_dontuse.h HPFS opt_dontuse.h LINPROCFS opt_dontuse.h MSDOSFS opt_dontuse.h NTFS opt_dontuse.h NULLFS opt_dontuse.h NWFS opt_dontuse.h PORTALFS opt_dontuse.h PROCFS opt_dontuse.h PSEUDOFS opt_dontuse.h SMBFS opt_dontuse.h UDF opt_dontuse.h UMAPFS opt_dontuse.h UNIONFS opt_dontuse.h # Broken - ffs_snapshot() dependency from ufs_lookup() :-( FFS opt_ffs_broken_fixme.h # These static filesystems have one slightly bogus static dependency in # sys/i386/i386/autoconf.c. If any of these filesystems are # statically compiled into the kernel, code for mounting them as root # filesystems will be enabled - but look below. NFSCLIENT opt_nfs.h NFSSERVER opt_nfs.h # filesystems and libiconv bridge CD9660_ICONV opt_dontuse.h MSDOSFS_ICONV opt_dontuse.h NTFS_ICONV opt_dontuse.h UDF_ICONV opt_dontuse.h # If you are following the conditions in the copyright, # you can enable soft-updates which will speed up a lot of thigs # and make the system safer from crashes at the same time. # otherwise a STUB module will be compiled in. SOFTUPDATES opt_ffs.h # Enabling this option turns on support for Access Control Lists in UFS, # which can be used to support high security configurations. Depends on # UFS_EXTATTR. UFS_ACL opt_ufs.h # Enabling this option turns on support for extended attributes in UFS-based # filesystems, which can be used to support high security configurations # as well as new filesystem features. UFS_EXTATTR opt_ufs.h UFS_EXTATTR_AUTOSTART opt_ufs.h # Enable fast hash lookups for large directories on UFS-based filesystems. UFS_DIRHASH opt_ufs.h # The below sentence is not in English, and neither is this one. # We plan to remove the static dependences above, with a # _ROOT option to control if it usable as root. This list # allows these options to be present in config files already (though # they won't make any difference yet). NFS_ROOT opt_nfsroot.h # SMB/CIFS requester NETSMB opt_netsmb.h NETSMBCRYPTO opt_netsmb.h # Options used only in subr_param.c. HZ opt_param.h MAXFILES opt_param.h NBUF opt_param.h NMBCLUSTERS opt_param.h NSFBUFS opt_param.h VM_BCACHE_SIZE_MAX opt_param.h VM_SWZONE_SIZE_MAX opt_param.h MAXUSERS DFLDSIZ opt_param.h MAXDSIZ opt_param.h MAXSSIZ opt_param.h # Generic SCSI options. CAM_MAX_HIGHPOWER opt_cam.h CAMDEBUG opt_cam.h CAM_DEBUG_DELAY opt_cam.h CAM_DEBUG_BUS opt_cam.h CAM_DEBUG_TARGET opt_cam.h CAM_DEBUG_LUN opt_cam.h CAM_DEBUG_FLAGS opt_cam.h CAM_NEW_TRAN_CODE opt_cam.h SCSI_DELAY opt_scsi.h SCSI_NO_SENSE_STRINGS opt_scsi.h SCSI_NO_OP_STRINGS opt_scsi.h # Options used only in cam/scsi/scsi_cd.c CHANGER_MIN_BUSY_SECONDS opt_cd.h CHANGER_MAX_BUSY_SECONDS opt_cd.h # Options used only in cam/scsi/scsi_sa.c. SA_IO_TIMEOUT opt_sa.h SA_SPACE_TIMEOUT opt_sa.h SA_REWIND_TIMEOUT opt_sa.h SA_ERASE_TIMEOUT opt_sa.h SA_1FM_AT_EOD opt_sa.h # Options used only in cam/scsi/scsi_pt.c SCSI_PT_DEFAULT_TIMEOUT opt_pt.h # Options used only in cam/scsi/scsi_ses.c SES_ENABLE_PASSTHROUGH opt_ses.h # Options used in dev/sym/ (Symbios SCSI driver). SYM_SETUP_LP_PROBE_MAP opt_sym.h #-Low Priority Probe Map (bits) # Allows the ncr to take precedence # 1 (1<<0) -> 810a, 860 # 2 (1<<1) -> 825a, 875, 885, 895 # 4 (1<<2) -> 895a, 896, 1510d SYM_SETUP_SCSI_DIFF opt_sym.h #-HVD support for 825a, 875, 885 # disabled:0 (default), enabled:1 SYM_SETUP_PCI_PARITY opt_sym.h #-PCI parity checking # disabled:0, enabled:1 (default) SYM_SETUP_MAX_LUN opt_sym.h #-Number of LUNs supported # default:8, range:[1..64] # Options used only in pci/ncr.c SCSI_NCR_DEBUG opt_ncr.h SCSI_NCR_MAX_SYNC opt_ncr.h SCSI_NCR_MAX_WIDE opt_ncr.h SCSI_NCR_MYADDR opt_ncr.h # Options used only in dev/isp/* ISP_TARGET_MODE opt_isp.h ISP_FW_CRASH_DUMP opt_isp.h # Options used in the 'ata' ATA/ATAPI driver ATA_STATIC_ID opt_ata.h ATA_NOPCI opt_ata.h DEV_ATADISK opt_ata.h DEV_ATAPICD opt_ata.h DEV_ATAPIST opt_ata.h DEV_ATAPIFD opt_ata.h DEV_ATAPICAM opt_ata.h DEV_ATARAID opt_ata.h # Net stuff. ACCEPT_FILTER_DATA ACCEPT_FILTER_HTTP BOOTP opt_bootp.h BOOTP_COMPAT opt_bootp.h BOOTP_NFSROOT opt_bootp.h BOOTP_NFSV3 opt_bootp.h BOOTP_WIRED_TO opt_bootp.h BRIDGE opt_bdg.h DEV_PF opt_pf.h DEV_PFLOG opt_pf.h DEV_PFSYNC opt_pf.h ETHER_II opt_ef.h ETHER_8023 opt_ef.h ETHER_8022 opt_ef.h ETHER_SNAP opt_ef.h MROUTING opt_mrouting.h PIM opt_mrouting.h INET opt_inet.h INET6 opt_inet6.h IPSEC opt_ipsec.h IPSEC_ESP opt_ipsec.h IPSEC_DEBUG opt_ipsec.h IPSEC_FILTERGIF opt_ipsec.h FAST_IPSEC opt_ipsec.h IPDIVERT DUMMYNET opt_ipdn.h IPFILTER opt_ipfilter.h IPFILTER_LOG opt_ipfilter.h IPFILTER_DEFAULT_BLOCK opt_ipfilter.h PFIL_HOOKS opt_pfil_hooks.h IPFIREWALL opt_ipfw.h IPFIREWALL_VERBOSE opt_ipfw.h IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h IPFIREWALL_DEFAULT_TO_ACCEPT opt_ipfw.h IPV6FIREWALL opt_ip6fw.h IPV6FIREWALL_VERBOSE opt_ip6fw.h IPV6FIREWALL_VERBOSE_LIMIT opt_ip6fw.h IPV6FIREWALL_DEFAULT_TO_ACCEPT opt_ip6fw.h IPSTEALTH IPX IPXIP opt_ipx.h LIBMBPOOL LIBMCHAIN MBUF_STRESS_TEST opt_mbuf_stress_test.h NCP NETATALK opt_atalk.h PPP_BSDCOMP opt_ppp.h PPP_DEFLATE opt_ppp.h PPP_FILTER opt_ppp.h RANDOM_IP_ID SLIP_IFF_OPTS opt_slip.h TCPDEBUG TCP_SIGNATURE opt_inet.h TCP_DROP_SYNFIN opt_tcp_input.h XBONEHACK # Netgraph(4). Use option NETGRAPH to enable the base netgraph code. # Each netgraph node type can be either be compiled into the kernel # or loaded dynamically. To get the former, include the corresponding # option below. Each type has its own man page, e.g. ng_async(4). NETGRAPH NETGRAPH_ASYNC opt_netgraph.h NETGRAPH_ATMLLC opt_netgraph.h NETGRAPH_ATM_ATMPIF opt_netgraph.h NETGRAPH_BLUETOOTH opt_netgraph.h NETGRAPH_BLUETOOTH_BT3C opt_netgraph.h NETGRAPH_BLUETOOTH_H4 opt_netgraph.h NETGRAPH_BLUETOOTH_HCI opt_netgraph.h NETGRAPH_BLUETOOTH_L2CAP opt_netgraph.h NETGRAPH_BLUETOOTH_SOCKET opt_netgraph.h NETGRAPH_BLUETOOTH_UBT opt_netgraph.h NETGRAPH_BLUETOOTH_UBTBCMFW opt_netgraph.h NETGRAPH_BPF opt_netgraph.h NETGRAPH_BRIDGE opt_netgraph.h NETGRAPH_CISCO opt_netgraph.h NETGRAPH_ECHO opt_netgraph.h NETGRAPH_ETHER opt_netgraph.h NETGRAPH_FRAME_RELAY opt_netgraph.h NETGRAPH_GIF opt_netgraph.h NETGRAPH_GIF_DEMUX opt_netgraph.h NETGRAPH_HOLE opt_netgraph.h NETGRAPH_IFACE opt_netgraph.h NETGRAPH_IP_INPUT opt_netgraph.h NETGRAPH_KSOCKET opt_netgraph.h NETGRAPH_L2TP opt_netgraph.h NETGRAPH_LMI opt_netgraph.h # MPPC compression requires proprietary files (not included) NETGRAPH_MPPC_COMPRESSION opt_netgraph.h NETGRAPH_MPPC_ENCRYPTION opt_netgraph.h NETGRAPH_ONE2MANY opt_netgraph.h NETGRAPH_PPP opt_netgraph.h NETGRAPH_PPPOE opt_netgraph.h NETGRAPH_PPTPGRE opt_netgraph.h NETGRAPH_RFC1490 opt_netgraph.h NETGRAPH_SOCKET opt_netgraph.h NETGRAPH_SPLIT opt_netgraph.h NETGRAPH_SPPP opt_netgraph.h NETGRAPH_TEE opt_netgraph.h NETGRAPH_TTY opt_netgraph.h NETGRAPH_UI opt_netgraph.h NETGRAPH_VJC opt_netgraph.h # NgATM options NGATM_ATM opt_netgraph.h NGATM_ATMBASE opt_netgraph.h NGATM_SSCOP opt_netgraph.h NGATM_SSCFU opt_netgraph.h NGATM_UNI opt_netgraph.h # DRM options DRM_DEBUG opt_drm.h ZERO_COPY_SOCKETS opt_zero.h TI_PRIVATE_JUMBOS opt_ti.h TI_JUMBO_HDRSPLIT opt_ti.h # ATM (HARP version) ATM_CORE opt_atm.h ATM_IP opt_atm.h ATM_SIGPVC opt_atm.h ATM_SPANS opt_atm.h ATM_UNI opt_atm.h # XXX Conflict: # of devices vs network protocol (Native ATM). # This makes "atm.h" unusable. NATM # DPT driver debug flags DPT_MEASURE_PERFORMANCE opt_dpt.h DPT_HANDLE_TIMEOUTS opt_dpt.h DPT_TIMEOUT_FACTOR opt_dpt.h DPT_LOST_IRQ opt_dpt.h DPT_RESET_HBA opt_dpt.h # Misc debug flags. Most of these should probably be replaced with # 'DEBUG', and then let people recompile just the interesting modules # with 'make CC="cc -DDEBUG"'. CLUSTERDEBUG opt_debug_cluster.h DEBUG_1284 opt_ppb_1284.h VP0_DEBUG opt_vpo.h LPT_DEBUG opt_lpt.h PLIP_DEBUG opt_plip.h LOCKF_DEBUG opt_debug_lockf.h NPX_DEBUG opt_debug_npx.h NETATALKDEBUG opt_atalk.h SI_DEBUG opt_debug_si.h SX_DEBUG opt_debug_sx.h # Fb options FB_DEBUG opt_fb.h FB_INSTALL_CDEV opt_fb.h # ppbus related options PERIPH_1284 opt_ppb_1284.h DONTPROBE_1284 opt_ppb_1284.h # smbus related options ENABLE_ALART opt_intpm.h # These cause changes all over the kernel BLKDEV_IOSIZE opt_global.h BURN_BRIDGES opt_global.h DEBUG opt_global.h DEBUG_LOCKS opt_global.h DEBUG_VFS_LOCKS opt_global.h DIAGNOSTIC opt_global.h INVARIANT_SUPPORT opt_global.h INVARIANTS opt_global.h LOOKUP_SHARED opt_global.h MCLSHIFT opt_global.h MUTEX_DEBUG opt_global.h MUTEX_NOINLINE opt_global.h MUTEX_PROFILING opt_global.h MSIZE opt_global.h REGRESSION opt_global.h RESTARTABLE_PANICS opt_global.h VFS_BIO_DEBUG opt_global.h # These are VM related options VM_KMEM_SIZE opt_vm.h VM_KMEM_SIZE_SCALE opt_vm.h VM_KMEM_SIZE_MAX opt_vm.h NO_SWAPPING opt_vm.h MALLOC_MAKE_FAILURES opt_vm.h MALLOC_PROFILE opt_vm.h PQ_NOOPT opt_vmpage.h PQ_NORMALCACHE opt_vmpage.h PQ_MEDIUMCACHE opt_vmpage.h PQ_LARGECACHE opt_vmpage.h PQ_HUGECACHE opt_vmpage.h PQ_CACHESIZE opt_vmpage.h # Standard SMP options SMP opt_global.h # Size of the kernel message buffer MSGBUF_SIZE opt_msgbuf.h # NFS options NFS_MINATTRTIMO opt_nfs.h NFS_MAXATTRTIMO opt_nfs.h NFS_MINDIRATTRTIMO opt_nfs.h NFS_MAXDIRATTRTIMO opt_nfs.h NFS_GATHERDELAY opt_nfs.h NFS_WDELAYHASHSIZ opt_nfs.h NFS_DEBUG opt_nfs.h # For the Bt848/Bt848A/Bt849/Bt878/Bt879 driver OVERRIDE_CARD opt_bktr.h OVERRIDE_TUNER opt_bktr.h OVERRIDE_DBX opt_bktr.h OVERRIDE_MSP opt_bktr.h BROOKTREE_SYSTEM_DEFAULT opt_bktr.h BROOKTREE_ALLOC_PAGES opt_bktr.h BKTR_OVERRIDE_CARD opt_bktr.h BKTR_OVERRIDE_TUNER opt_bktr.h BKTR_OVERRIDE_DBX opt_bktr.h BKTR_OVERRIDE_MSP opt_bktr.h BKTR_SYSTEM_DEFAULT opt_bktr.h BKTR_ALLOC_PAGES opt_bktr.h BKTR_USE_PLL opt_bktr.h BKTR_GPIO_ACCESS opt_bktr.h BKTR_NO_MSP_RESET opt_bktr.h BKTR_430_FX_MODE opt_bktr.h BKTR_SIS_VIA_MODE opt_bktr.h BKTR_USE_FREEBSD_SMBUS opt_bktr.h BKTR_NEW_MSP34XX_DRIVER opt_bktr.h # options for serial support COM_ESP opt_sio.h COM_MULTIPORT opt_sio.h BREAK_TO_DEBUGGER opt_comconsole.h ALT_BREAK_TO_DEBUGGER opt_comconsole.h # Options to support PPS UART_PPS_ON_CTS opt_uart.h # options for bus/device framework BUS_DEBUG opt_bus.h # options for USB support USB_DEBUG opt_usb.h USBVERBOSE opt_usb.h UKBD_DFLT_KEYMAP opt_ukbd.h UPLCOM_INTR_INTERVAL opt_uplcom.h UVSCOM_DEFAULT_OPKTSIZE opt_uvscom.h UVSCOM_INTR_INTERVAL opt_uvscom.h # Vinum options VINUMDEBUG opt_vinum.h # Embedded system options INIT_PATH opt_init_path.h ROOTDEVNAME opt_rootdevname.h FDC_DEBUG opt_fdc.h PCFCLOCK_VERBOSE opt_pcfclock.h PCFCLOCK_MAX_RETRIES opt_pcfclock.h TDFX_LINUX opt_tdfx.h KTR opt_global.h KTR_ALQ opt_ktr.h KTR_MASK opt_ktr.h KTR_CPUMASK opt_ktr.h KTR_COMPILE opt_global.h KTR_ENTRIES opt_global.h KTR_VERBOSE opt_ktr.h WITNESS opt_global.h WITNESS_DDB opt_witness.h WITNESS_SKIPSPIN opt_witness.h # options for ACPI support ACPI_DEBUG opt_acpi.h ACPI_MAX_THREADS opt_acpi.h ACPI_NO_SEMAPHORES opt_acpi.h ACPICA_PEDANTIC opt_acpi.h # options for DEVFS, see sys/fs/devfs/devfs.h NDEVFSINO opt_devfs.h NDEVFSOVERFLOW opt_devfs.h # various 'device presence' options. DEV_BPF opt_bpf.h DEV_ISA opt_isa.h DEV_MCA opt_mca.h DEV_SPLASH opt_splash.h EISA_SLOTS opt_eisa.h # ed driver ED_NO_MIIBUS opt_ed.h # wi driver WI_SYMBOL_FIRMWARE opt_wi.h # XXX bogusly global. DEVICE_POLLING opt_global.h SOCKBUF_DEBUG opt_global.h # options for ubsec driver UBSEC_DEBUG opt_ubsec.h UBSEC_RNDTEST opt_ubsec.h UBSEC_NO_RNG opt_ubsec.h # options for hifn driver HIFN_DEBUG opt_hifn.h HIFN_RNDTEST opt_hifn.h # options for safenet driver SAFE_DEBUG opt_safe.h SAFE_NO_RNG opt_safe.h SAFE_RNDTEST opt_safe.h # syscons options MAXCONS opt_syscons.h SC_ALT_MOUSE_IMAGE opt_syscons.h SC_CUT_SPACES2TABS opt_syscons.h SC_CUT_SEPCHARS opt_syscons.h SC_DEBUG_LEVEL opt_syscons.h SC_DFLT_FONT opt_syscons.h SC_DISABLE_DDBKEY opt_syscons.h SC_DISABLE_REBOOT opt_syscons.h SC_HISTORY_SIZE opt_syscons.h SC_KERNEL_CONS_ATTR opt_syscons.h SC_KERNEL_CONS_REV_ATTR opt_syscons.h SC_MOUSE_CHAR opt_syscons.h SC_NO_CUTPASTE opt_syscons.h SC_NO_FONT_LOADING opt_syscons.h SC_NO_HISTORY opt_syscons.h SC_NO_SUSPEND_VTYSWITCH opt_syscons.h SC_NO_SYSMOUSE opt_syscons.h SC_NORM_ATTR opt_syscons.h SC_NORM_REV_ATTR opt_syscons.h SC_PIXEL_MODE opt_syscons.h SC_RENDER_DEBUG opt_syscons.h SC_TWOBUTTON_MOUSE opt_syscons.h # kbd options KBD_DISABLE_KEYMAP_LOAD opt_kbd.h KBD_INSTALL_CDEV opt_kbd.h KBD_MAXRETRY opt_kbd.h KBD_MAXWAIT opt_kbd.h KBD_RESETDELAY opt_kbd.h KBDIO_DEBUG opt_kbd.h # options for the Atheros HAL (only useful with source code) AH_SUPPORT_AR5210 opt_ah.h AH_SUPPORT_AR5211 opt_ah.h AH_SUPPORT_AR5212 opt_ah.h AH_DEBUG opt_ah.h AH_DEBUG_ALQ opt_ah.h AH_ASSERT opt_ah.h # dcons options DCONS_BUF_SIZE opt_dcons.h DCONS_POLL_HZ opt_dcons.h DCONS_FORCE_CONSOLE opt_dcons.h DCONS_FORCE_GDB opt_dcons.h Index: head/sys/dev/cy/cy.c =================================================================== --- head/sys/dev/cy/cy.c (revision 130343) +++ head/sys/dev/cy/cy.c (revision 130344) @@ -1,2875 +1,2875 @@ /*- * cyclades cyclom-y serial driver * Andrew Herbert , 17 August 1993 * * Copyright (c) 1993 Andrew Herbert. * All rights reserved. * * 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. The name Andrew Herbert may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``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 I 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" /* * TODO: * Atomic COR change. * Consoles. */ /* * Temporary compile-time configuration options. */ #define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) /* Number of chars in the receiver FIFO before an * an interrupt is generated. Should depend on * line speed. Needs to be about 6 on a 486DX33 * for 4 active ports at 115200 bps. Why doesn't * 10 work? */ #define PollMode /* Use polling-based irq service routine, not the * hardware svcack lines. Must be defined for * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, * and stops 4 * 115200 bps from working. */ #undef Smarts /* Enable slightly more CD1400 intelligence. Mainly * the output CR/LF processing, plus we can avoid a * few checks usually done in ttyinput(). * * XXX not fully implemented, and not particularly * worthwhile. */ #undef CyDebug /* Include debugging code (not very expensive). */ /* These will go away. */ #undef SOFT_CTS_OFLOW #define SOFT_HOTCHAR #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NCY 10 /* KLUDGE */ /* * Dictionary so that I can name everything *sio* or *com* to compare with * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to * simplify the comparision. These will go away. */ #define LSR_BI CD1400_RDSR_BREAK #define LSR_FE CD1400_RDSR_FE #define LSR_OE CD1400_RDSR_OE #define LSR_PE CD1400_RDSR_PE #define MCR_DTR CD1400_MSVR2_DTR #define MCR_RTS CD1400_MSVR1_RTS #define MSR_CTS CD1400_MSVR2_CTS #define MSR_DCD CD1400_MSVR2_CD #define MSR_DSR CD1400_MSVR2_DSR #define MSR_RI CD1400_MSVR2_RI #define NSIO (NCY * CY_MAX_PORTS) #define comconsole cyconsole #define comdefaultrate cydefaultrate #define com_events cy_events #define comhardclose cyhardclose #define commctl cymctl #define comparam cyparam #define comspeed cyspeed #define comstart cystart #define comwakeup cywakeup #define p_com_addr p_cy_addr #define sioclose cyclose #define siodriver cydriver #define siodtrwakeup cydtrwakeup #define sioinput cyinput #define siointr1 cyintr #define sioioctl cyioctl #define sioopen cyopen #define siopoll cypoll #define siosettimeout cysettimeout #define siosetwater cysetwater #define comstop cystop #define siowrite cywrite #define sio_fast_ih cy_fast_ih #define sio_inited cy_inited #define sio_irec cy_irec #define sio_lock cy_lock #define sio_slow_ih cy_slow_ih #define sio_timeout cy_timeout #define sio_timeout_handle cy_timeout_handle #define sio_timeouts_until_log cy_timeouts_until_log #define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) /* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ #define CD1400_xIVR_CHAN_SHIFT 3 #define CD1400_xIVR_CHAN 0x1F /* * ETC states. com->etc may also contain a hardware ETC command value, * meaning that execution of that command is pending. */ #define ETC_NONE 0 /* we depend on bzero() setting this */ #define ETC_BREAK_STARTING 1 #define ETC_BREAK_STARTED 2 #define ETC_BREAK_ENDING 3 #define ETC_BREAK_ENDED 4 #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) /* * Not all of the magic is parametrized in the following macros. 16 and * 0xff are related to the bitfields in a udev_t. CY_MAX_PORTS must be * ((0xff & ~MINOR_MAGIC_MASK) + 1) for things to work. */ #define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \ | (((mynor) & 0xff) & ~MINOR_MAGIC_MASK)) #define UNIT_TO_MINOR(unit) (((unit) / CY_MAX_PORTS) << 16 \ | (((unit) & 0xff) & ~MINOR_MAGIC_MASK)) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * comstop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_ODONE 1 /* output transmitted */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) #ifdef SMP #define COM_LOCK() mtx_lock_spin(&sio_lock) #define COM_UNLOCK() mtx_unlock_spin(&sio_lock) #else #define COM_LOCK() #define COM_UNLOCK() #endif /* types. XXX - should be elsewhere */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ #if 0 u_char cfcr_image; /* copy of value written to CFCR */ #endif u_char etc; /* pending Embedded Transmit Command */ u_char extra_state; /* more flag bits, separate for order trick */ #if 0 u_char fifo_image; /* copy of value written to FIFO */ #endif u_char gfrcr_image; /* copy of value read from GFRCR */ #if 0 bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ #endif u_char mcr_dtr; /* MCR bit that is wired to DTR */ u_char mcr_image; /* copy of value written to MCR */ u_char mcr_rts; /* MCR bit that is wired to RTS */ #if 0 #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ #endif int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ #if 0 u_int tx_fifo_size; #endif u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ibufold; /* old input buffer, to be freed */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ int ibufsize; /* size of ibuf (not include error bytes) */ int ierroff; /* offset of error bytes in ibuf */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ int cy_align; /* index for register alignment */ cy_addr cy_iobase; /* base address of this port's cyclom */ cy_addr iobase; /* base address of this port's cd1400 */ int mcr_rts_reg; /* cd1400 reg number of reg holding mcr_rts */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; u_int recv_exception; /* exception chars received */ u_int mdm; /* modem signal changes */ #ifdef CyDebug u_int start_count; /* no. of calls to comstart() */ u_int start_real; /* no. of calls that did something */ #endif u_char car; /* CD1400 CAR shadow (if first unit in cd) */ u_char channel_control;/* CD1400 CCR control command shadow */ u_char cor[3]; /* CD1400 COR1-3 shadows */ u_char intr_enable; /* CD1400 SRER shadow */ /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; }; devclass_t cy_devclass; char cy_driver_name[] = "cy"; static void cd1400_channel_cmd(struct com_s *com, int cmd); static void cd1400_channel_cmd_wait(struct com_s *com); static void cd_etc(struct com_s *com, int etc); static int cd_getreg(struct com_s *com, int reg); static void cd_setreg(struct com_s *com, int reg, int val); static timeout_t siodtrwakeup; static void comhardclose(struct com_s *com); static void sioinput(struct com_s *com); static int commctl(struct com_s *com, int bits, int how); static int comparam(struct tty *tp, struct termios *t); static void siopoll(void *arg); static void siosettimeout(void); static int siosetwater(struct com_s *com, speed_t speed); static int comspeed(speed_t speed, u_long cy_clock, int *prescaler_io); static void comstart(struct tty *tp); static void comstop(struct tty *tp, int rw); static timeout_t comwakeup; static void disc_optim(struct tty *tp, struct termios *t, struct com_s *com); #ifdef CyDebug void cystatus(int unit); #endif static struct mtx sio_lock; static int sio_inited; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) static d_open_t sioopen; static d_close_t sioclose; static d_write_t siowrite; static d_ioctl_t sioioctl; static struct cdevsw sio_cdevsw = { .d_version = D_VERSION, .d_open = sioopen, .d_close = sioclose, .d_write = siowrite, .d_ioctl = sioioctl, .d_name = cy_driver_name, .d_flags = D_TTY | D_NEEDGIANT, }; static int comconsole = -1; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static void *sio_fast_ih; static void *sio_slow_ih; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #ifdef CyDebug static u_int cd_inbs; static u_int cy_inbs; static u_int cd_outbs; static u_int cy_outbs; static u_int cy_svrr_probes; static u_int cy_timeouts; #endif static int cy_chip_offset[] = { 0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00, }; static int cy_nr_cd1400s[NCY]; static int cy_total_devices; #undef RxFifoThreshold static int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); int cy_units(cy_iobase, cy_align) cy_addr cy_iobase; int cy_align; { int cyu; u_char firmware_version; int i; cy_addr iobase; for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) { iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align); /* wait for chip to become ready for new command */ for (i = 0; i < 10; i++) { DELAY(50); if (!cd_inb(iobase, CD1400_CCR, cy_align)) break; } /* clear the GFRCR register */ cd_outb(iobase, CD1400_GFRCR, cy_align, 0); /* issue a reset command */ cd_outb(iobase, CD1400_CCR, cy_align, CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); /* XXX bogus initialization to avoid a gcc bug/warning. */ firmware_version = 0; /* wait for the CD1400 to initialize itself */ for (i = 0; i < 200; i++) { DELAY(50); /* retrieve firmware version */ firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); if ((firmware_version & 0xf0) == 0x40) break; } /* * Anything in the 0x40-0x4F range is fine. * If one CD1400 is bad then we don't support higher * numbered good ones on this board. */ if ((firmware_version & 0xf0) != 0x40) break; } return (cyu); } void * cyattach_common(cy_iobase, cy_align) cy_addr cy_iobase; int cy_align; { int adapter; int cyu; u_char firmware_version; cy_addr iobase; int minorbase; int ncyu; int unit; while (sio_inited != 2) if (atomic_cmpset_int(&sio_inited, 0, 1)) { mtx_init(&sio_lock, cy_driver_name, NULL, MTX_SPIN); atomic_store_rel_int(&sio_inited, 2); } adapter = cy_total_devices; if ((u_int)adapter >= NCY) { printf( "cy%d: can't attach adapter: insufficient cy devices configured\n", adapter); return (NULL); } ncyu = cy_units(cy_iobase, cy_align); if (ncyu == 0) return (NULL); cy_nr_cd1400s[adapter] = ncyu; cy_total_devices++; unit = adapter * CY_MAX_PORTS; for (cyu = 0; cyu < ncyu; ++cyu) { int cdu; iobase = (cy_addr) (cy_iobase + (cy_chip_offset[cyu] << cy_align)); firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); /* Set up a receive timeout period of than 1+ ms. */ cd_outb(iobase, CD1400_PPR, cy_align, howmany(CY_CLOCK(firmware_version) / CD1400_PPR_PRESCALER, 1000)); for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { struct com_s *com; int s; com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT | M_ZERO); if (com == NULL) break; com->unit = unit; com->gfrcr_image = firmware_version; if (CY_RTS_DTR_SWAPPED(firmware_version)) { com->mcr_dtr = MCR_RTS; com->mcr_rts = MCR_DTR; com->mcr_rts_reg = CD1400_MSVR2; } else { com->mcr_dtr = MCR_DTR; com->mcr_rts = MCR_RTS; com->mcr_rts_reg = CD1400_MSVR1; } com->dtr_wait = 3 * hz; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->cy_align = cy_align; com->cy_iobase = cy_iobase; com->iobase = iobase; com->car = ~CD1400_CAR_CHAN; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } if (siosetwater(com, com->it_in.c_ispeed) != 0) { free(com, M_DEVBUF); return (NULL); } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; s = spltty(); com_addr(unit) = com; splx(s); if (sio_fast_ih == NULL) { swi_add(&tty_ithd, "tty:cy", siopoll, NULL, SWI_TTY, 0, &sio_fast_ih); swi_add(&clk_ithd, "tty:cy", siopoll, NULL, SWI_TTY, 0, &sio_slow_ih); } minorbase = UNIT_TO_MINOR(unit); make_dev(&sio_cdevsw, minorbase, UID_ROOT, GID_WHEEL, 0600, "ttyc%r%r", adapter, unit % CY_MAX_PORTS); make_dev(&sio_cdevsw, minorbase | CONTROL_INIT_STATE, UID_ROOT, GID_WHEEL, 0600, "ttyic%r%r", adapter, unit % CY_MAX_PORTS); make_dev(&sio_cdevsw, minorbase | CONTROL_LOCK_STATE, UID_ROOT, GID_WHEEL, 0600, "ttylc%r%r", adapter, unit % CY_MAX_PORTS); make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK, UID_UUCP, GID_DIALER, 0660, "cuac%r%r", adapter, unit % CY_MAX_PORTS); make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_INIT_STATE, UID_UUCP, GID_DIALER, 0660, "cuaic%r%r", adapter, unit % CY_MAX_PORTS); make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_LOCK_STATE, UID_UUCP, GID_DIALER, 0660, "cualc%r%r", adapter, unit % CY_MAX_PORTS); } } /* ensure an edge for the next interrupt */ cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); return (com_addr(adapter * CY_MAX_PORTS)); } static int sioopen(dev, flag, mode, td) dev_t dev; int flag; int mode; struct thread *td; { struct com_s *com; int error; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (mynor & CONTROL_MASK) return (0); tp = dev->si_tty = com->tp = ttymalloc(com->tp); s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "cybi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && suser(td)) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_stop = comstop; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; /* Encode per-board unit in LIVR for access in intr routines. */ cd_setreg(com, CD1400_LIVR, (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); #if 0 com->poll = com->no_irq; com->poll_output = com->loses_outints; #endif ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; #if 0 if (com->hasfifo) { /* * (Re)enable and flush fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } critical_enter(); COM_LOCK(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); COM_UNLOCK(); critical_exit(); #else /* !0 */ /* * Flush fifos. This requires a full channel reset which * also disables the transmitter and receiver. Recover * from this. */ cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_CHANRESET); cd1400_channel_cmd(com, com->channel_control); critical_enter(); COM_LOCK(); com->prev_modem_status = com->last_modem_status = cd_getreg(com, CD1400_MSVR2); cd_setreg(com, CD1400_SRER, com->intr_enable = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); COM_UNLOCK(); critical_exit(); #endif /* 0 */ /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "cybi" * instead of "cydcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) ttyld_modem(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); --com->wopeners; if (error != 0) goto out; goto open_top; } error = ttyld_open(tp, dev); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, td) dev_t dev; int flag; int mode; struct thread *td; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); cd_etc(com, CD1400_ETC_STOPBREAK); ttyld_close(tp, flag); disc_optim(tp, &tp->t_termios, com); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); com->tp = NULL; #endif return (0); } static void comhardclose(com) struct com_s *com; { cy_addr iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); #if 0 com->poll = FALSE; com->poll_output = FALSE; #endif com->do_timestamp = 0; #if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #else /* XXX */ critical_enter(); COM_LOCK(); com->etc = ETC_NONE; cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC); COM_UNLOCK(); critical_exit(); cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); #endif { #if 0 outb(iobase + com_ier, 0); #else critical_enter(); COM_LOCK(); cd_setreg(com, CD1400_SRER, com->intr_enable = 0); COM_UNLOCK(); critical_exit(); #endif tp = com->tp; if ((tp->t_cflag & HUPCL) /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || (!com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); /* Disable receiver (leave transmitter enabled). */ com->channel_control = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | CD1400_CCR_RCVDIS; cd1400_channel_cmd(com, com->channel_control); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } #if 0 if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } #endif com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else return (ttyld_write(tp, uio, flag)); #endif } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } /* * This function: * a) needs to be called with COM_LOCK() held, and * b) needs to return with COM_LOCK() held. */ static void sioinput(com) struct com_s *com; { u_char *buf; int incc; u_char line_status; int recv_data; struct tty *tp; buf = com->ibuf; tp = com->tp; if (!(tp->t_state & TS_ISOPEN)) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; return; } if (tp->t_state & TS_CAN_BYPASS_L_RINT) { /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ do { /* * This may look odd, but it is using save-and-enable * semantics instead of the save-and-disable semantics * that are used everywhere else. */ COM_UNLOCK(); critical_exit(); incc = com->iptr - buf; if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); buf += incc; tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } critical_enter(); COM_LOCK(); } while (buf < com->iptr); } else { do { /* * This may look odd, but it is using save-and-enable * semantics instead of the save-and-disable semantics * that are used everywhere else. */ COM_UNLOCK(); critical_exit(); line_status = buf[com->ierroff]; recv_data = *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } ttyld_rint(tp, recv_data); critical_enter(); COM_LOCK(); } while (buf < com->iptr); } com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; /* * There is now room for another low-level buffer full of input, * so enable RTS if it is now disabled and there is room in the * high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) && !(tp->t_state & TS_TBLOCK)) #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_setreg(com, com->mcr_rts_reg, com->mcr_image |= com->mcr_rts); #endif } void siointr1(vcom) void *vcom; { struct com_s *basecom; int baseu; int cy_align; cy_addr cy_iobase; int cyu; cy_addr iobase; u_char status; int unit; COM_LOCK(); /* XXX could this be placed down lower in the loop? */ basecom = (struct com_s *)vcom; baseu = basecom->unit; cy_align = basecom->cy_align; cy_iobase = basecom->cy_iobase; unit = baseu / CY_MAX_PORTS; /* check each CD1400 in turn */ for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) { iobase = (cy_addr) (cy_iobase + (cy_chip_offset[cyu] << cy_align)); /* poll to see if it has any work */ status = cd_inb(iobase, CD1400_SVRR, cy_align); if (status == 0) continue; #ifdef CyDebug ++cy_svrr_probes; #endif /* service requests as appropriate, giving priority to RX */ if (status & CD1400_SVRR_RXRDY) { struct com_s *com; u_int count; u_char *ioptr; u_char line_status; u_char recv_data; u_char serv_type; #ifdef PollMode u_char save_rir; #endif #ifdef PollMode save_rir = cd_inb(iobase, CD1400_RIR, cy_align); /* enter rx service */ cd_outb(iobase, CD1400_CAR, cy_align, save_rir); com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car = save_rir & CD1400_CAR_CHAN; serv_type = cd_inb(iobase, CD1400_RIVR, cy_align); com = com_addr(baseu + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #else /* ack receive service */ serv_type = cy_inb(iobase, CY8_SVCACKR, cy_align); com = com_addr(baseu + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif if (serv_type & CD1400_RIVR_EXCEPTION) { ++com->recv_exception; line_status = cd_inb(iobase, CD1400_RDSR, cy_align); /* break/unnattached error bits or real input? */ recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifndef SOFT_HOTCHAR if (line_status & CD1400_RDSR_SPECIAL && com->hotchar != 0) swi_sched(sio_fast_ih, 0); #endif #if 1 /* XXX "intelligent" PFO error handling would break O error handling */ if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { /* Don't store PE if IGNPAR and BI if IGNBRK, this hack allows "raw" tty optimization works even if IGN* is set. */ if ( com->tp == NULL || !(com->tp->t_state & TS_ISOPEN) || ((line_status & (LSR_PE|LSR_FE)) && (com->tp->t_iflag & IGNPAR)) || ((line_status & LSR_BI) && (com->tp->t_iflag & IGNBRK))) goto cont; if ( (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) && ((line_status & LSR_FE) || ((line_status & LSR_PE) && (com->tp->t_iflag & INPCK)))) recv_data = 0; } #endif /* 1 */ ++com->bytes_in; #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) swi_sched(sio_fast_ih, 0); #endif ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; ioptr[0] = recv_data; ioptr[com->ierroff] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, cy_align, com->mcr_image &= ~com->mcr_rts); #endif if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } goto cont; } else { int ifree; count = cd_inb(iobase, CD1400_RDCR, cy_align); if (!count) goto cont; com->bytes_in += count; ioptr = com->iptr; ifree = com->ibufend - ioptr; if (count > ifree) { count -= ifree; com_events += ifree; if (ifree != 0) { if (com->do_timestamp) microtime(&com->timestamp); do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) swi_sched(sio_fast_ih, 0); #endif ioptr[0] = recv_data; ioptr[com->ierroff] = 0; ++ioptr; } while (--ifree != 0); } com->delta_error_counts [CE_INTERRUPT_BUF_OVERFLOW] += count; do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) swi_sched(sio_fast_ih, 0); #endif } while (--count != 0); } else { if (com->do_timestamp) microtime(&com->timestamp); if (ioptr <= com->ihighwater && ioptr + count > com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, cy_align, com->mcr_image &= ~com->mcr_rts); #endif com_events += count; do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) swi_sched(sio_fast_ih, 0); #endif ioptr[0] = recv_data; ioptr[com->ierroff] = 0; ++ioptr; } while (--count != 0); } com->iptr = ioptr; } cont: /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_RIR, cy_align, save_rir & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } if (status & CD1400_SVRR_MDMCH) { struct com_s *com; u_char modem_status; #ifdef PollMode u_char save_mir; #else u_char vector; #endif #ifdef PollMode save_mir = cd_inb(iobase, CD1400_MIR, cy_align); /* enter modem service */ cd_outb(iobase, CD1400_CAR, cy_align, save_mir); com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car = save_mir & CD1400_CAR_CHAN; com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + (save_mir & CD1400_MIR_CHAN)); #else /* ack modem service */ vector = cy_inb(iobase, CY8_SVCACKM, cy_align); com = com_addr(baseu + ((vector >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif ++com->mdm; modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; swi_sched(sio_fast_ih, 0); } #ifdef SOFT_CTS_OFLOW /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) { com->state |= CS_ODEVREADY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) && !(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable = com->intr_enable & ~CD1400_SRER_TXMPTY | CD1400_SRER_TXRDY); } else { com->state &= ~CS_ODEVREADY; if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable = com->intr_enable & ~CD1400_SRER_TXRDY | CD1400_SRER_TXMPTY); } } #endif } /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_MIR, cy_align, save_mir & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } if (status & CD1400_SVRR_TXRDY) { struct com_s *com; #ifdef PollMode u_char save_tir; #else u_char vector; #endif #ifdef PollMode save_tir = cd_inb(iobase, CD1400_TIR, cy_align); /* enter tx service */ cd_outb(iobase, CD1400_CAR, cy_align, save_tir); com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car = save_tir & CD1400_CAR_CHAN; com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + (save_tir & CD1400_TIR_CHAN)); #else /* ack transmit service */ vector = cy_inb(iobase, CY8_SVCACKT, cy_align); com = com_addr(baseu + ((vector >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif if (com->etc != ETC_NONE) { if (com->intr_enable & CD1400_SRER_TXRDY) { /* * Here due to sloppy SRER_TXRDY * enabling. Ignore. Come back when * tx is empty. */ cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY); goto terminate_tx_service; } switch (com->etc) { case CD1400_ETC_SENDBREAK: case CD1400_ETC_STOPBREAK: /* * Start the command. Come back on * next tx empty interrupt, hopefully * after command has been executed. */ cd_outb(iobase, CD1400_COR2, cy_align, com->cor[1] |= CD1400_COR2_ETC); cd_outb(iobase, CD1400_TDR, cy_align, CD1400_ETC_CMD); cd_outb(iobase, CD1400_TDR, cy_align, com->etc); if (com->etc == CD1400_ETC_SENDBREAK) com->etc = ETC_BREAK_STARTING; else com->etc = ETC_BREAK_ENDING; goto terminate_tx_service; case ETC_BREAK_STARTING: /* * BREAK is now on. Continue with * SRER_TXMPTY processing, hopefully * don't come back. */ com->etc = ETC_BREAK_STARTED; break; case ETC_BREAK_STARTED: /* * Came back due to sloppy SRER_TXMPTY * enabling. Hope again. */ break; case ETC_BREAK_ENDING: /* * BREAK is now off. Continue with * SRER_TXMPTY processing and don't * come back. The SWI handler will * restart tx interrupts if necessary. */ cd_outb(iobase, CD1400_COR2, cy_align, com->cor[1] &= ~CD1400_COR2_ETC); com->etc = ETC_BREAK_ENDED; if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; swi_sched(sio_fast_ih, 0); } break; case ETC_BREAK_ENDED: /* * Shouldn't get here. Hope again. */ break; } } if (com->intr_enable & CD1400_SRER_TXMPTY) { if (!(com->extra_state & CSE_ODONE)) { com_events += LOTS_OF_EVENTS; com->extra_state |= CSE_ODONE; swi_sched(sio_fast_ih, 0); } cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable &= ~CD1400_SRER_TXMPTY); goto terminate_tx_service; } if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { u_char *ioptr; u_int ocount; ioptr = com->obufq.l_head; ocount = com->obufq.l_tail - ioptr; if (ocount > CD1400_TX_FIFO_SIZE) ocount = CD1400_TX_FIFO_SIZE; com->bytes_out += ocount; do cd_outb(iobase, CD1400_TDR, cy_align, *ioptr++); while (--ocount != 0); com->obufq.l_head = ioptr; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ com->state &= ~CS_BUSY; /* * The setting of CSE_ODONE may be * stale here. We currently only * use it when CS_BUSY is set, and * fixing it when we clear CS_BUSY * is easiest. */ if (com->extra_state & CSE_ODONE) { com_events -= LOTS_OF_EVENTS; com->extra_state &= ~CSE_ODONE; } cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY); } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; /* handle at high level ASAP */ swi_sched(sio_fast_ih, 0); } } } /* terminate service context */ terminate_tx_service: #ifdef PollMode cd_outb(iobase, CD1400_TIR, cy_align, save_tir & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } } /* ensure an edge for the next interrupt */ cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); swi_sched(sio_slow_ih, SWI_DELAY); COM_UNLOCK(); } static int sioioctl(dev, cmd, data, flag, td) dev_t dev; u_long cmd; caddr_t data; int flag; struct thread *td; { struct com_s *com; int error; int mynor; int s; struct tty *tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(td); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } tp = com->tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = ttyioctl(dev, cmd, data, flag, td); disc_optim(tp, &tp->t_termios, com); if (error != ENOTTY) return (error); s = spltty(); switch (cmd) { case TIOCSBRK: #if 0 outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); #else cd_etc(com, CD1400_ETC_SENDBREAK); #endif break; case TIOCCBRK: #if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #else cd_etc(com, CD1400_ETC_STOPBREAK); #endif break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(td); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll(void *arg) { int unit; #ifdef CyDebug ++cy_timeouts; #endif if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { struct com_s *com; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ critical_enter(); COM_LOCK(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; COM_UNLOCK(); critical_exit(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } if (com->iptr != com->ibuf) { critical_enter(); COM_LOCK(); sioinput(com); COM_UNLOCK(); critical_exit(); } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; critical_enter(); COM_LOCK(); sioinput(com); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; COM_UNLOCK(); critical_exit(); if (delta_modem_status & MSR_DCD) ttyld_modem(tp, com->prev_modem_status & MSR_DCD); } if (com->extra_state & CSE_ODONE) { critical_enter(); COM_LOCK(); com_events -= LOTS_OF_EVENTS; com->extra_state &= ~CSE_ODONE; COM_UNLOCK(); critical_exit(); if (!(com->state & CS_BUSY)) { tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); } if (com->etc != ETC_NONE) { if (com->etc == ETC_BREAK_ENDED) com->etc = ETC_NONE; wakeup(&com->etc); } } if (com->state & CS_ODONE) { critical_enter(); COM_LOCK(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; COM_UNLOCK(); critical_exit(); ttyld_start(tp); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { int bits; int cflag; struct com_s *com; u_char cor_change; u_long cy_clock; int idivisor; int iflag; int iprescaler; int itimeout; int odivisor; int oprescaler; u_char opt; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); /* check requested parameters */ cy_clock = CY_CLOCK(com->gfrcr_image); idivisor = comspeed(t->c_ispeed, cy_clock, &iprescaler); if (idivisor <= 0) return (EINVAL); odivisor = comspeed(t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed, cy_clock, &oprescaler); if (odivisor <= 0) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ s = spltty(); if (t->c_ospeed == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); (void) siosetwater(com, t->c_ispeed); /* XXX we don't actually change the speed atomically. */ cd_setreg(com, CD1400_RBPR, idivisor); cd_setreg(com, CD1400_RCOR, iprescaler); cd_setreg(com, CD1400_TBPR, odivisor); cd_setreg(com, CD1400_TCOR, oprescaler); /* * channel control * receiver enable * transmitter enable (always set) */ cflag = t->c_cflag; opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); if (opt != com->channel_control) { com->channel_control = opt; cd1400_channel_cmd(com, opt); } #ifdef Smarts /* set special chars */ /* XXX if one is _POSIX_VDISABLE, can't use some others */ if (t->c_cc[VSTOP] != _POSIX_VDISABLE) cd_setreg(com, CD1400_SCHR1, t->c_cc[VSTOP]); if (t->c_cc[VSTART] != _POSIX_VDISABLE) cd_setreg(com, CD1400_SCHR2, t->c_cc[VSTART]); if (t->c_cc[VINTR] != _POSIX_VDISABLE) cd_setreg(com, CD1400_SCHR3, t->c_cc[VINTR]); if (t->c_cc[VSUSP] != _POSIX_VDISABLE) cd_setreg(com, CD1400_SCHR4, t->c_cc[VSUSP]); #endif /* * set channel option register 1 - * parity mode * stop bits * char length */ opt = 0; /* parity */ if (cflag & PARENB) { if (cflag & PARODD) opt |= CD1400_COR1_PARODD; opt |= CD1400_COR1_PARNORMAL; } iflag = t->c_iflag; if (!(iflag & INPCK)) opt |= CD1400_COR1_NOINPCK; bits = 1 + 1; /* stop bits */ if (cflag & CSTOPB) { ++bits; opt |= CD1400_COR1_STOP2; } /* char length */ switch (cflag & CSIZE) { case CS5: bits += 5; opt |= CD1400_COR1_CS5; break; case CS6: bits += 6; opt |= CD1400_COR1_CS6; break; case CS7: bits += 7; opt |= CD1400_COR1_CS7; break; default: bits += 8; opt |= CD1400_COR1_CS8; break; } cor_change = 0; if (opt != com->cor[0]) { cor_change |= CD1400_CCR_COR1; cd_setreg(com, CD1400_COR1, com->cor[0] = opt); } /* * Set receive time-out period, normally to max(one char time, 5 ms). */ itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; #ifdef SOFT_HOTCHAR #define MIN_RTP 1 #else #define MIN_RTP 5 #endif if (itimeout < MIN_RTP) itimeout = MIN_RTP; if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 && t->c_cc[VTIME] * 10 > itimeout) itimeout = t->c_cc[VTIME] * 10; if (itimeout > 255) itimeout = 255; cd_setreg(com, CD1400_RTPR, itimeout); /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) opt |= CD1400_COR2_IXANY; if (iflag & IXOFF) opt |= CD1400_COR2_IXOFF; #endif #ifndef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_COR2_CCTS_OFLOW; #endif critical_enter(); COM_LOCK(); if (opt != com->cor[1]) { cor_change |= CD1400_CCR_COR2; cd_setreg(com, CD1400_COR2, com->cor[1] = opt); } COM_UNLOCK(); critical_exit(); /* * set channel option register 3 - * receiver FIFO interrupt threshold * flow control */ opt = RxFifoThreshold; #ifdef Smarts if (t->c_lflag & ICANON) opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ if (iflag & IXOFF) /* detect and transparently handle START and STOP chars */ opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; #endif if (opt != com->cor[2]) { cor_change |= CD1400_CCR_COR3; cd_setreg(com, CD1400_COR3, com->cor[2] = opt); } /* notify the CD1400 if COR1-3 have changed */ if (cor_change) cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | cor_change); /* * set channel option register 4 - * CR/NL processing * break processing * received exception processing */ opt = 0; if (iflag & IGNCR) opt |= CD1400_COR4_IGNCR; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to * have ICRNL && INLCR being done in both layers, or to have * synchronisation problems */ if (iflag & ICRNL) opt |= CD1400_COR4_ICRNL; if (iflag & INLCR) opt |= CD1400_COR4_INLCR; #endif if (iflag & IGNBRK) opt |= CD1400_COR4_IGNBRK | CD1400_COR4_NOBRKINT; /* * The `-ignbrk -brkint parmrk' case is not handled by the hardware, * so only tell the hardware about -brkint if -parmrk. */ if (!(iflag & (BRKINT | PARMRK))) opt |= CD1400_COR4_NOBRKINT; #if 0 /* XXX using this "intelligence" breaks reporting of overruns. */ if (iflag & IGNPAR) opt |= CD1400_COR4_PFO_DISCARD; else { if (iflag & PARMRK) opt |= CD1400_COR4_PFO_ESC; else opt |= CD1400_COR4_PFO_NUL; } #else opt |= CD1400_COR4_PFO_EXCEPTION; #endif cd_setreg(com, CD1400_COR4, opt); /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) opt |= CD1400_COR5_ISTRIP; if (t->c_iflag & IEXTEN) /* enable LNEXT (e.g. ctrl-v quoting) handling */ opt |= CD1400_COR5_LNEXT; #ifdef Smarts if (t->c_oflag & ONLCR) opt |= CD1400_COR5_ONLCR; if (t->c_oflag & OCRNL) opt |= CD1400_COR5_OCRNL; #endif cd_setreg(com, CD1400_COR5, opt); /* * We always generate modem status change interrupts for CD changes. * Among other things, this is necessary to track TS_CARR_ON for * pstat to print even when the driver doesn't care. CD changes * should be rare so interrupts for them are not worth extra code to * avoid. We avoid interrupts for other modem status changes (except * for CTS changes when SOFT_CTS_OFLOW is configured) since this is * simplest and best. */ /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ opt = CD1400_MCOR1_CDzd; #ifdef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_MCOR1_CTSzd; #endif cd_setreg(com, CD1400_MCOR1, opt); /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ opt = CD1400_MCOR2_CDod; #ifdef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_MCOR2_CTSod; #endif cd_setreg(com, CD1400_MCOR2, opt); /* * XXX should have done this long ago, but there is too much state * to change all atomically. */ critical_enter(); COM_LOCK(); com->state &= ~CS_TTGO; if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_setreg(com, com->mcr_rts_reg, com->mcr_image |= com->mcr_rts); #endif } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; #ifdef SOFT_CTS_OFLOW com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } #endif /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); #if 0 /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); #endif if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { if (!(com->intr_enable & CD1400_SRER_TXRDY)) cd_setreg(com, CD1400_SRER, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXMPTY) | CD1400_SRER_TXRDY); } else { if (com->intr_enable & CD1400_SRER_TXRDY) cd_setreg(com, CD1400_SRER, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY); } COM_UNLOCK(); critical_exit(); splx(s); comstart(tp); if (com->ibufold != NULL) { free(com->ibufold, M_DEVBUF); com->ibufold = NULL; } return (0); } static int siosetwater(com, speed) struct com_s *com; speed_t speed; { int cp4ticks; u_char *ibuf; int ibufsize; struct tty *tp; /* * Make the buffer size large enough to handle a softtty interrupt * latency of about 2 ticks without loss of throughput or data * (about 3 ticks if input flow control is not used or not honoured, * but a bit less for CS5-CS7 modes). */ cp4ticks = speed / 10 / hz * 4; for (ibufsize = 128; ibufsize < cp4ticks;) ibufsize <<= 1; if (ibufsize == com->ibufsize) { return (0); } /* * Allocate input buffer. The extra factor of 2 in the size is * to allow for an error byte for each input byte. */ ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); if (ibuf == NULL) { return (ENOMEM); } /* Initialize non-critical variables. */ com->ibufold = com->ibuf; com->ibufsize = ibufsize; tp = com->tp; if (tp != NULL) { tp->t_ififosize = 2 * ibufsize; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; } /* * Read current input buffer, if any. Continue with interrupts * disabled. */ critical_enter(); COM_LOCK(); if (com->iptr != com->ibuf) sioinput(com); /*- * Initialize critical variables, including input buffer watermarks. * The external device is asked to stop sending when the buffer * exactly reaches high water, or when the high level requests it. * The high level is notified immediately (rather than at a later * clock tick) when this watermark is reached. * The buffer size is chosen so the watermark should almost never * be reached. * The low watermark is invisibly 0 since the buffer is always * emptied all at once. */ com->iptr = com->ibuf = ibuf; com->ibufend = ibuf + ibufsize; com->ierroff = ibufsize; com->ihighwater = ibuf + 3 * ibufsize / 4; COM_UNLOCK(); critical_exit(); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; #ifdef CyDebug bool_t started; #endif int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); #ifdef CyDebug ++com->start_count; started = FALSE; #endif critical_enter(); COM_LOCK(); if (tp->t_state & TS_TTSTOP) { com->state &= ~CS_TTGO; if (com->intr_enable & CD1400_SRER_TXRDY) cd_setreg(com, CD1400_SRER, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY); } else { com->state |= CS_TTGO; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) && !(com->intr_enable & CD1400_SRER_TXRDY)) cd_setreg(com, CD1400_SRER, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXMPTY) | CD1400_SRER_TXRDY); } if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_setreg(com, com->mcr_rts_reg, com->mcr_image &= ~com->mcr_rts); #endif } else { if (!(com->mcr_image & com->mcr_rts) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_setreg(com, com->mcr_rts_reg, com->mcr_image |= com->mcr_rts); #endif } COM_UNLOCK(); critical_exit(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { #ifdef CyDebug started = TRUE; #endif com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; critical_enter(); COM_LOCK(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) cd_setreg(com, CD1400_SRER, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXMPTY) | CD1400_SRER_TXRDY); } COM_UNLOCK(); critical_exit(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { #ifdef CyDebug started = TRUE; #endif com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; critical_enter(); COM_LOCK(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) cd_setreg(com, CD1400_SRER, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXMPTY) | CD1400_SRER_TXRDY); } COM_UNLOCK(); critical_exit(); } tp->t_state |= TS_BUSY; } #ifdef CyDebug if (started) ++com->start_real; #endif #if 0 critical_enter(); COM_LOCK(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ COM_UNLOCK(); critical_exit(); #endif ttwwakeup(tp); splx(s); } static void comstop(tp, rw) struct tty *tp; int rw; { struct com_s *com; bool_t wakeup_etc; com = com_addr(DEV_TO_UNIT(tp->t_dev)); wakeup_etc = FALSE; critical_enter(); COM_LOCK(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->extra_state & CSE_ODONE) { com_events -= LOTS_OF_EVENTS; com->extra_state &= ~CSE_ODONE; if (com->etc != ETC_NONE) { if (com->etc == ETC_BREAK_ENDED) com->etc = ETC_NONE; wakeup_etc = TRUE; } } com->tp->t_state &= ~TS_BUSY; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); } if (rw & FREAD) { /* XXX no way to reset only input fifo. */ com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } COM_UNLOCK(); critical_exit(); if (wakeup_etc) wakeup(&com->etc); if (rw & FWRITE && com->etc == ETC_NONE) cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); comstart(tp); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { if (com->channel_control & CD1400_CCR_RCVEN) bits |= TIOCM_LE; mcr = com->mcr_image; if (mcr & com->mcr_dtr) bits |= TIOCM_DTR; if (mcr & com->mcr_rts) /* XXX wired on for Cyclom-8Ys */ bits |= TIOCM_RTS; /* * We must read the modem status from the hardware because * we don't generate modem status change interrupts for all * changes, so com->prev_modem_status is not guaranteed to * be up to date. This is safe, unlike for sio, because * reading the status register doesn't clear pending modem * status change interrupts. */ msr = cd_getreg(com, CD1400_MSVR2); if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; if (msr & MSR_RI) /* XXX not connected except for Cyclom-16Y? */ bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= com->mcr_dtr; if (bits & TIOCM_RTS) mcr |= com->mcr_rts; critical_enter(); COM_LOCK(); switch (how) { case DMSET: com->mcr_image = mcr; cd_setreg(com, CD1400_MSVR1, mcr); cd_setreg(com, CD1400_MSVR2, mcr); break; case DMBIS: com->mcr_image = mcr = com->mcr_image | mcr; cd_setreg(com, CD1400_MSVR1, mcr); cd_setreg(com, CD1400_MSVR2, mcr); break; case DMBIC: com->mcr_image = mcr = com->mcr_image & ~mcr; cd_setreg(com, CD1400_MSVR1, mcr); cd_setreg(com, CD1400_MSVR2, mcr); break; } COM_UNLOCK(); critical_exit(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN) { someopen = TRUE; #if 0 if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } #endif } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); #if 0 /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { critical_enter(); COM_LOCK(); siointr1(com); COM_UNLOCK(); critical_exit(); } } #endif /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; critical_enter(); COM_LOCK(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; COM_UNLOCK(); critical_exit(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { #ifndef SOFT_HOTCHAR u_char opt; #endif com->hotchar = ttyldoptim(tp); #ifndef SOFT_HOTCHAR opt = com->cor[2] & ~CD1400_COR3_SCD34; if (com->hotchar != 0) { cd_setreg(com, CD1400_SCHR3, com->hotchar); cd_setreg(com, CD1400_SCHR4, com->hotchar); opt |= CD1400_COR3_SCD34; } if (opt != com->cor[2]) { cd_setreg(com, CD1400_COR3, com->cor[2] = opt); cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); } #endif } #ifdef Smarts /* standard line discipline input routine */ int cyinput(c, tp) int c; struct tty *tp; { /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK * bits, as they are done by the CD1400. Hardly worth the effort, * given that high-throughput sessions are raw anyhow. */ } #endif /* Smarts */ static int comspeed(speed, cy_clock, prescaler_io) speed_t speed; u_long cy_clock; int *prescaler_io; { int actual; int error; int divider; int prescaler; int prescaler_unit; if (speed == 0) return (0); if (speed < 0 || speed > 150000) return (-1); /* determine which prescaler to use */ for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; prescaler_unit--, prescaler >>= 2) { if (cy_clock / prescaler / speed > 63) break; } divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */ if (divider > 255) divider = 255; actual = cy_clock/prescaler/divider; /* 10 times error in percent: */ error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2; /* 3.0% max error tolerance */ if (error < -30 || error > 30) return (-1); #if 0 printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); printf("divider = %d (%x)\n", divider, divider); printf("actual = %d\n", actual); printf("error = %d\n", error); #endif *prescaler_io = prescaler_unit; return (divider); } static void cd1400_channel_cmd(com, cmd) struct com_s *com; int cmd; { cd1400_channel_cmd_wait(com); cd_setreg(com, CD1400_CCR, cmd); cd1400_channel_cmd_wait(com); } static void cd1400_channel_cmd_wait(com) struct com_s *com; { struct timeval start; struct timeval tv; long usec; if (cd_getreg(com, CD1400_CCR) == 0) return; microtime(&start); for (;;) { if (cd_getreg(com, CD1400_CCR) == 0) return; microtime(&tv); usec = 1000000 * (tv.tv_sec - start.tv_sec) + tv.tv_usec - start.tv_usec; if (usec >= 5000) { log(LOG_ERR, "cy%d: channel command timeout (%ld usec)\n", com->unit, usec); return; } } } static void cd_etc(com, etc) struct com_s *com; int etc; { /* * We can't change the hardware's ETC state while there are any * characters in the tx fifo, since those characters would be * interpreted as commands! Unputting characters from the fifo * is difficult, so we wait up to 12 character times for the fifo * to drain. The command will be delayed for up to 2 character * times for the tx to become empty. Unputting characters from * the tx holding and shift registers is impossible, so we wait * for the tx to become empty so that the command is sure to be * executed soon after we issue it. */ critical_enter(); COM_LOCK(); if (com->etc == etc) goto wait; if ((etc == CD1400_ETC_SENDBREAK && (com->etc == ETC_BREAK_STARTING || com->etc == ETC_BREAK_STARTED)) || (etc == CD1400_ETC_STOPBREAK && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED || com->etc == ETC_NONE))) { COM_UNLOCK(); critical_exit(); return; } com->etc = etc; cd_setreg(com, CD1400_SRER, com->intr_enable = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY); wait: COM_UNLOCK(); critical_exit(); while (com->etc == etc && tsleep(&com->etc, TTIPRI | PCATCH, "cyetc", 0) == 0) continue; } static int cd_getreg(com, reg) struct com_s *com; int reg; { struct com_s *basecom; u_char car; int cy_align; cy_addr iobase; #ifdef SMP int need_unlock; #endif int val; basecom = com_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1)); car = com->unit & CD1400_CAR_CHAN; cy_align = com->cy_align; iobase = com->iobase; critical_enter(); #ifdef SMP need_unlock = 0; if (!mtx_owned(&sio_lock)) { COM_LOCK(); need_unlock = 1; } #endif if (basecom->car != car) cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car); val = cd_inb(iobase, reg, cy_align); #ifdef SMP if (need_unlock) COM_UNLOCK(); #endif critical_exit(); return (val); } static void cd_setreg(com, reg, val) struct com_s *com; int reg; int val; { struct com_s *basecom; u_char car; int cy_align; cy_addr iobase; #ifdef SMP int need_unlock; #endif basecom = com_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1)); car = com->unit & CD1400_CAR_CHAN; cy_align = com->cy_align; iobase = com->iobase; critical_enter(); #ifdef SMP need_unlock = 0; if (!mtx_owned(&sio_lock)) { COM_LOCK(); need_unlock = 1; } #endif if (basecom->car != car) cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car); cd_outb(iobase, reg, cy_align, val); #ifdef SMP if (need_unlock) COM_UNLOCK(); #endif critical_exit(); } #ifdef CyDebug /* useful in ddb */ void cystatus(unit) int unit; { struct com_s *com; cy_addr iobase; u_int ocount; struct tty *tp; com = com_addr(unit); printf("info for channel %d\n", unit); printf("------------------\n"); printf("total cyclom service probes:\t%d\n", cy_svrr_probes); printf("calls to upper layer:\t\t%d\n", cy_timeouts); if (com == NULL) return; iobase = com->iobase; printf("\n"); printf("cd1400 base address:\\tt%p\n", iobase); printf("saved channel_control:\t\t0x%02x\n", com->channel_control); printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", com->cor[0], com->cor[1], com->cor[2]); printf("service request enable reg:\t0x%02x (0x%02x cached)\n", cd_getreg(com, CD1400_SRER), com->intr_enable); printf("service request register:\t0x%02x\n", cd_inb(iobase, CD1400_SVRR, com->cy_align)); printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", cd_getreg(com, CD1400_MSVR2), com->prev_modem_status); printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", cd_inb(iobase, CD1400_RIR, com->cy_align), cd_inb(iobase, CD1400_TIR, com->cy_align), cd_inb(iobase, CD1400_MIR, com->cy_align)); printf("\n"); printf("com state:\t\t\t0x%02x\n", com->state); printf("calls to comstart():\t\t%d (%d useful)\n", com->start_count, com->start_real); printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); ocount = 0; if (com->obufs[0].l_queued) ocount += com->obufs[0].l_tail - com->obufs[0].l_head; if (com->obufs[1].l_queued) ocount += com->obufs[1].l_tail - com->obufs[1].l_head; printf("tx buffer chars:\t\t%u\n", ocount); printf("received chars:\t\t\t%d\n", com->bytes_in); printf("received exceptions:\t\t%d\n", com->recv_exception); printf("modem signal deltas:\t\t%d\n", com->mdm); printf("transmitted chars:\t\t%d\n", com->bytes_out); printf("\n"); tp = com->tp; if (tp != NULL) { printf("tty state:\t\t\t0x%08x\n", tp->t_state); printf( "upper layer queue lengths:\t%d raw, %d canon, %d output\n", tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); } else printf("tty state:\t\t\tclosed\n"); } #endif /* CyDebug */ Index: head/sys/dev/digi/digi.c =================================================================== --- head/sys/dev/digi/digi.c (revision 130343) +++ head/sys/dev/digi/digi.c (revision 130344) @@ -1,1930 +1,1930 @@ /*- * Copyright (c) 2001 Brian Somers * based on work by Slawa Olhovchenkov * John Prince * Eric Hernes * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ /*- * TODO: * Figure out what the con bios stuff is supposed to do * Test with *LOTS* more cards - I only have a PCI8r and an ISA Xem. */ #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CTRL_DEV 0x800000 #define CALLOUT_MASK 0x400000 #define CONTROL_INIT_STATE 0x100000 #define CONTROL_LOCK_STATE 0x200000 #define CONTROL_MASK (CTRL_DEV|CONTROL_INIT_STATE|CONTROL_LOCK_STATE) #define UNIT_MASK 0x030000 #define PORT_MASK 0x0000FF #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) (((mynor) & UNIT_MASK)>>16) #define MINOR_TO_PORT(mynor) ((mynor) & PORT_MASK) static d_open_t digiopen; static d_close_t digiclose; static d_read_t digiread; static d_write_t digiwrite; static d_ioctl_t digiioctl; static void digistop(struct tty *tp, int rw); static int digimctl(struct digi_p *port, int bits, int how); static void digi_poll(void *ptr); static void digi_freemoduledata(struct digi_softc *); static void fepcmd(struct digi_p *port, int cmd, int op, int ncmds); static void digistart(struct tty *tp); static int digiparam(struct tty *tp, struct termios *t); static void digihardclose(struct digi_p *port); static void digi_intr(void *); static int digi_init(struct digi_softc *_sc); static int digi_loadmoduledata(struct digi_softc *); static int digi_inuse(struct digi_softc *); static void digi_free_state(struct digi_softc *); #define fepcmd_b(port, cmd, op1, op2, ncmds) \ fepcmd(port, cmd, (op2 << 8) | op1, ncmds) #define fepcmd_w fepcmd static speed_t digidefaultrate = TTYDEF_SPEED; struct con_bios { struct con_bios *next; u_char *bios; size_t size; }; static struct con_bios *con_bios_list; devclass_t digi_devclass; static char driver_name[] = "digi"; unsigned digi_debug = 0; static struct speedtab digispeedtab[] = { { 0, 0}, /* old (sysV-like) Bx codes */ { 50, 1}, { 75, 2}, { 110, 3}, { 134, 4}, { 150, 5}, { 200, 6}, { 300, 7}, { 600, 8}, { 1200, 9}, { 1800, 10}, { 2400, 11}, { 4800, 12}, { 9600, 13}, { 19200, 14}, { 38400, 15}, { 57600, (02000 | 1)}, { 76800, (02000 | 2)}, { 115200, (02000 | 3)}, { 230400, (02000 | 6)}, { -1, -1} }; const struct digi_control_signals digi_xixe_signals = { 0x02, 0x08, 0x10, 0x20, 0x40, 0x80 }; const struct digi_control_signals digi_normal_signals = { 0x02, 0x80, 0x20, 0x10, 0x40, 0x01 }; static struct cdevsw digi_sw = { .d_version = D_VERSION, .d_open = digiopen, .d_close = digiclose, .d_read = digiread, .d_write = digiwrite, .d_ioctl = digiioctl, .d_name = driver_name, .d_flags = D_TTY | D_NEEDGIANT, }; static void digi_poll(void *ptr) { struct digi_softc *sc; sc = (struct digi_softc *)ptr; callout_handle_init(&sc->callout); digi_intr(sc); sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1); } static void digi_int_test(void *v) { struct digi_softc *sc = v; callout_handle_init(&sc->inttest); #ifdef DIGI_INTERRUPT if (sc->intr_timestamp.tv_sec || sc->intr_timestamp.tv_usec) { /* interrupt OK! */ return; } log(LOG_ERR, "digi%d: Interrupt didn't work, use polled mode\n", unit); #endif sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1); } static void digi_freemoduledata(struct digi_softc *sc) { if (sc->fep.data != NULL) { free(sc->fep.data, M_TTYS); sc->fep.data = NULL; } if (sc->link.data != NULL) { free(sc->link.data, M_TTYS); sc->link.data = NULL; } if (sc->bios.data != NULL) { free(sc->bios.data, M_TTYS); sc->bios.data = NULL; } } static int digi_bcopy(const void *vfrom, void *vto, size_t sz) { volatile const char *from = (volatile const char *)vfrom; volatile char *to = (volatile char *)vto; size_t i; for (i = 0; i < sz; i++) *to++ = *from++; from = (const volatile char *)vfrom; to = (volatile char *)vto; for (i = 0; i < sz; i++) if (*to++ != *from++) return (0); return (1); } void digi_delay(struct digi_softc *sc, const char *txt, u_long timo) { if (cold) DELAY(timo * 1000000 / hz); else tsleep(sc, PUSER | PCATCH, txt, timo); } static int digi_init(struct digi_softc *sc) { int i, cnt, resp; u_char *ptr; int lowwater; struct digi_p *port; volatile struct board_chan *bc; ptr = NULL; if (sc->status == DIGI_STATUS_DISABLED) { log(LOG_ERR, "digi%d: Cannot init a disabled card\n", sc->res.unit); return (EIO); } if (sc->bios.data == NULL) { log(LOG_ERR, "digi%d: Cannot init without BIOS\n", sc->res.unit); return (EIO); } #if 0 if (sc->link.data == NULL && sc->model >= PCCX) { log(LOG_ERR, "digi%d: Cannot init without link info\n", sc->res.unit); return (EIO); } #endif if (sc->fep.data == NULL) { log(LOG_ERR, "digi%d: Cannot init without fep code\n", sc->res.unit); return (EIO); } sc->status = DIGI_STATUS_NOTINIT; if (sc->numports) { /* * We're re-initialising - maybe because someone's attached * another port module. For now, we just re-initialise * everything. */ if (digi_inuse(sc)) return (EBUSY); digi_free_state(sc); } ptr = sc->setwin(sc, MISCGLOBAL); for (i = 0; i < 16; i += 2) vW(ptr + i) = 0; switch (sc->model) { case PCXEVE: outb(sc->wport, 0xff); /* window 7 */ ptr = sc->vmem + (BIOSCODE & 0x1fff); if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) { device_printf(sc->dev, "BIOS upload failed\n"); return (EIO); } outb(sc->port, FEPCLR); break; case PCXE: case PCXI: case PCCX: ptr = sc->setwin(sc, BIOSCODE + ((0xf000 - sc->mem_seg) << 4)); if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) { device_printf(sc->dev, "BIOS upload failed\n"); return (EIO); } break; case PCXEM: case PCIEPCX: case PCIXR: if (sc->pcibus) PCIPORT = FEPRST; else outb(sc->port, FEPRST | FEPMEM); for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & FEPMASK) != FEPRST; i++) { if (i > hz) { log(LOG_ERR, "digi%d: %s init reset failed\n", sc->res.unit, sc->name); return (EIO); } digi_delay(sc, "digiinit0", 5); } DLOG(DIGIDB_INIT, (sc->dev, "Got init reset after %d us\n", i)); /* Now upload the BIOS */ cnt = (sc->bios.size < sc->win_size - BIOSOFFSET) ? sc->bios.size : sc->win_size - BIOSOFFSET; ptr = sc->setwin(sc, BIOSOFFSET); if (!digi_bcopy(sc->bios.data, ptr, cnt)) { device_printf(sc->dev, "BIOS upload (1) failed\n"); return (EIO); } if (cnt != sc->bios.size) { /* and the second part */ ptr = sc->setwin(sc, sc->win_size); if (!digi_bcopy(sc->bios.data + cnt, ptr, sc->bios.size - cnt)) { device_printf(sc->dev, "BIOS upload failed\n"); return (EIO); } } ptr = sc->setwin(sc, 0); vW(ptr + 0) = 0x0401; vW(ptr + 2) = 0x0bf0; vW(ptr + 4) = 0x0000; vW(ptr + 6) = 0x0000; break; } DLOG(DIGIDB_INIT, (sc->dev, "BIOS uploaded\n")); ptr = sc->setwin(sc, MISCGLOBAL); W(ptr) = 0; if (sc->pcibus) { PCIPORT = FEPCLR; resp = FEPRST; } else if (sc->model == PCXEVE) { outb(sc->port, FEPCLR); resp = FEPRST; } else { outb(sc->port, FEPCLR | FEPMEM); resp = FEPRST | FEPMEM; } for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & FEPMASK) == resp; i++) { if (i > hz) { log(LOG_ERR, "digi%d: BIOS start failed\n", sc->res.unit); return (EIO); } digi_delay(sc, "digibios0", 5); } DLOG(DIGIDB_INIT, (sc->dev, "BIOS started after %d us\n", i)); for (i = 0; vW(ptr) != *(u_short *)"GD"; i++) { if (i > 2*hz) { log(LOG_ERR, "digi%d: BIOS boot failed " "(0x%02x != 0x%02x)\n", sc->res.unit, vW(ptr), *(u_short *)"GD"); return (EIO); } digi_delay(sc, "digibios1", 5); } DLOG(DIGIDB_INIT, (sc->dev, "BIOS booted after %d iterations\n", i)); if (sc->link.data != NULL) { DLOG(DIGIDB_INIT, (sc->dev, "Loading link data\n")); ptr = sc->setwin(sc, 0xcd0); digi_bcopy(sc->link.data, ptr, 21); /* XXX 21 ? */ } /* load FEP/OS */ switch (sc->model) { case PCXE: case PCXEVE: case PCXI: ptr = sc->setwin(sc, sc->model == PCXI ? 0x2000 : 0x0); digi_bcopy(sc->fep.data, ptr, sc->fep.size); /* A BIOS request to move our data to 0x2000 */ ptr = sc->setwin(sc, MBOX); vW(ptr + 0) = 2; vW(ptr + 2) = sc->mem_seg + FEPCODESEG; vW(ptr + 4) = 0; vW(ptr + 6) = FEPCODESEG; vW(ptr + 8) = 0; vW(ptr + 10) = sc->fep.size; /* Run the BIOS request */ outb(sc->port, FEPREQ | FEPMEM); outb(sc->port, FEPCLR | FEPMEM); for (i = 0; W(ptr); i++) { if (i > hz) { log(LOG_ERR, "digi%d: FEP/OS move failed\n", sc->res.unit); sc->hidewin(sc); return (EIO); } digi_delay(sc, "digifep0", 5); } DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS moved after %d iterations\n", i)); /* Clear the confirm word */ ptr = sc->setwin(sc, FEPSTAT); vW(ptr + 0) = 0; /* A BIOS request to execute the FEP/OS */ ptr = sc->setwin(sc, MBOX); vW(ptr + 0) = 0x01; vW(ptr + 2) = FEPCODESEG; vW(ptr + 4) = 0x04; /* Run the BIOS request */ outb(sc->port, FEPREQ); outb(sc->port, FEPCLR); ptr = sc->setwin(sc, FEPSTAT); break; case PCXEM: case PCIEPCX: case PCIXR: DLOG(DIGIDB_INIT, (sc->dev, "Loading FEP/OS\n")); cnt = (sc->fep.size < sc->win_size - BIOSOFFSET) ? sc->fep.size : sc->win_size - BIOSOFFSET; ptr = sc->setwin(sc, BIOSOFFSET); digi_bcopy(sc->fep.data, ptr, cnt); if (cnt != sc->fep.size) { ptr = sc->setwin(sc, BIOSOFFSET + cnt); digi_bcopy(sc->fep.data + cnt, ptr, sc->fep.size - cnt); } DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS loaded\n")); ptr = sc->setwin(sc, 0xc30); W(ptr + 4) = 0x1004; W(ptr + 6) = 0xbfc0; W(ptr + 0) = 0x03; W(ptr + 2) = 0x00; /* Clear the confirm word */ ptr = sc->setwin(sc, FEPSTAT); W(ptr + 0) = 0; if (sc->port) outb(sc->port, 0); /* XXX necessary ? */ break; case PCCX: ptr = sc->setwin(sc, 0xd000); digi_bcopy(sc->fep.data, ptr, sc->fep.size); /* A BIOS request to execute the FEP/OS */ ptr = sc->setwin(sc, 0xc40); W(ptr + 0) = 1; W(ptr + 2) = FEPCODE >> 4; W(ptr + 4) = 4; /* Clear the confirm word */ ptr = sc->setwin(sc, FEPSTAT); W(ptr + 0) = 0; /* Run the BIOS request */ outb(sc->port, FEPREQ | FEPMEM); /* send interrupt to BIOS */ outb(sc->port, FEPCLR | FEPMEM); break; } /* Now wait 'till the FEP/OS has booted */ for (i = 0; vW(ptr) != *(u_short *)"OS"; i++) { if (i > 2*hz) { log(LOG_ERR, "digi%d: FEP/OS start failed " "(0x%02x != 0x%02x)\n", sc->res.unit, vW(ptr), *(u_short *)"OS"); sc->hidewin(sc); return (EIO); } digi_delay(sc, "digifep1", 5); } DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS started after %d iterations\n", i)); if (sc->model >= PCXEM) { ptr = sc->setwin(sc, 0xe04); vW(ptr) = 2; ptr = sc->setwin(sc, 0xc02); sc->numports = vW(ptr); } else { ptr = sc->setwin(sc, 0xc22); sc->numports = vW(ptr); } if (sc->numports == 0) { device_printf(sc->dev, "%s, 0 ports found\n", sc->name); sc->hidewin(sc); return (0); } if (sc->numports > 256) { /* Our minor numbering scheme is broken for more than 256 */ device_printf(sc->dev, "%s, 256 ports (%d ports found)\n", sc->name, sc->numports); sc->numports = 256; } else device_printf(sc->dev, "%s, %d ports found\n", sc->name, sc->numports); if (sc->ports) free(sc->ports, M_TTYS); sc->ports = malloc(sizeof(struct digi_p) * sc->numports, M_TTYS, M_WAITOK | M_ZERO); if (sc->ttys) free(sc->ttys, M_TTYS); sc->ttys = malloc(sizeof(struct tty) * sc->numports, M_TTYS, M_WAITOK | M_ZERO); /* * XXX Should read port 0xc90 for an array of 2byte values, 1 per * port. If the value is 0, the port is broken.... */ ptr = sc->setwin(sc, 0); /* We should now init per-port structures */ bc = (volatile struct board_chan *)(ptr + CHANSTRUCT); sc->gdata = (volatile struct global_data *)(ptr + FEP_GLOBAL); sc->memcmd = ptr + sc->gdata->cstart; sc->memevent = ptr + sc->gdata->istart; for (i = 0; i < sc->numports; i++, bc++) { port = sc->ports + i; port->pnum = i; port->sc = sc; port->status = ENABLED; port->tp = sc->ttys + i; port->bc = bc; if (sc->model == PCXEVE) { port->txbuf = ptr + (((bc->tseg - sc->mem_seg) << 4) & 0x1fff); port->rxbuf = ptr + (((bc->rseg - sc->mem_seg) << 4) & 0x1fff); port->txwin = FEPWIN | ((bc->tseg - sc->mem_seg) >> 9); port->rxwin = FEPWIN | ((bc->rseg - sc->mem_seg) >> 9); } else if (sc->model == PCXI || sc->model == PCXE) { port->txbuf = ptr + ((bc->tseg - sc->mem_seg) << 4); port->rxbuf = ptr + ((bc->rseg - sc->mem_seg) << 4); port->txwin = port->rxwin = 0; } else { port->txbuf = ptr + (((bc->tseg - sc->mem_seg) << 4) % sc->win_size); port->rxbuf = ptr + (((bc->rseg - sc->mem_seg) << 4) % sc->win_size); port->txwin = FEPWIN | (((bc->tseg - sc->mem_seg) << 4) / sc->win_size); port->rxwin = FEPWIN | (((bc->rseg - sc->mem_seg) << 4) / sc->win_size); } port->txbufsize = bc->tmax + 1; port->rxbufsize = bc->rmax + 1; lowwater = port->txbufsize >> 2; if (lowwater > 1024) lowwater = 1024; sc->setwin(sc, 0); fepcmd_w(port, STXLWATER, lowwater, 10); fepcmd_w(port, SRXLWATER, port->rxbufsize >> 2, 10); fepcmd_w(port, SRXHWATER, (3 * port->rxbufsize) >> 2, 10); bc->edelay = 100; port->dtr_wait = 3 * hz; /* * We don't use all the flags from since * they are only relevant for logins. It's important to have * echo off initially so that the line doesn't start blathering * before the echo flag can be turned off. */ port->it_in.c_iflag = 0; port->it_in.c_oflag = 0; port->it_in.c_cflag = TTYDEF_CFLAG; port->it_in.c_lflag = 0; termioschars(&port->it_in); port->it_in.c_ispeed = port->it_in.c_ospeed = digidefaultrate; port->it_out = port->it_in; port->send_ring = 1; /* Default action on signal RI */ port->dev[0] = make_dev(&digi_sw, (sc->res.unit << 16) + i, UID_ROOT, GID_WHEEL, 0600, "ttyD%d.%d", sc->res.unit, i); port->dev[1] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | CONTROL_INIT_STATE, UID_ROOT, GID_WHEEL, 0600, "ttyiD%d.%d", sc->res.unit, i); port->dev[2] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | CONTROL_LOCK_STATE, UID_ROOT, GID_WHEEL, 0600, "ttylD%d.%d", sc->res.unit, i); port->dev[3] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | CALLOUT_MASK, UID_UUCP, GID_DIALER, 0660, "cuaD%d.%d", sc->res.unit, i); port->dev[4] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | CALLOUT_MASK | CONTROL_INIT_STATE, UID_UUCP, GID_DIALER, 0660, "cuaiD%d.%d", sc->res.unit, i); port->dev[5] = make_dev(&digi_sw, ((sc->res.unit << 16) + i) | CALLOUT_MASK | CONTROL_LOCK_STATE, UID_UUCP, GID_DIALER, 0660, "cualD%d.%d", sc->res.unit, i); } sc->hidewin(sc); sc->inttest = timeout(digi_int_test, sc, hz); /* fepcmd_w(&sc->ports[0], 0xff, 0, 0); */ sc->status = DIGI_STATUS_ENABLED; return (0); } static int digimctl(struct digi_p *port, int bits, int how) { int mstat; if (how == DMGET) { port->sc->setwin(port->sc, 0); mstat = port->bc->mstat; port->sc->hidewin(port->sc); bits = TIOCM_LE; if (mstat & port->sc->csigs->rts) bits |= TIOCM_RTS; if (mstat & port->cd) bits |= TIOCM_CD; if (mstat & port->dsr) bits |= TIOCM_DSR; if (mstat & port->sc->csigs->cts) bits |= TIOCM_CTS; if (mstat & port->sc->csigs->ri) bits |= TIOCM_RI; if (mstat & port->sc->csigs->dtr) bits |= TIOCM_DTR; return (bits); } /* Only DTR and RTS may be set */ mstat = 0; if (bits & TIOCM_DTR) mstat |= port->sc->csigs->dtr; if (bits & TIOCM_RTS) mstat |= port->sc->csigs->rts; switch (how) { case DMSET: fepcmd_b(port, SETMODEM, mstat, ~mstat, 0); break; case DMBIS: fepcmd_b(port, SETMODEM, mstat, 0, 0); break; case DMBIC: fepcmd_b(port, SETMODEM, 0, mstat, 0); break; } return (0); } static int digiopen(dev_t dev, int flag, int mode, struct thread *td) { struct digi_softc *sc; struct tty *tp; int unit; int pnum; struct digi_p *port; int s; int error, mynor; volatile struct board_chan *bc; error = 0; mynor = minor(dev); unit = MINOR_TO_UNIT(minor(dev)); pnum = MINOR_TO_PORT(minor(dev)); sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); if (!sc) return (ENXIO); if (sc->status != DIGI_STATUS_ENABLED) { DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n")); return (ENXIO); } if (pnum >= sc->numports) { DLOG(DIGIDB_OPEN, (sc->dev, "port%d: Doesn't exist\n", pnum)); return (ENXIO); } if (mynor & (CTRL_DEV | CONTROL_MASK)) { sc->opencnt++; return (0); } port = &sc->ports[pnum]; tp = dev->si_tty = port->tp; bc = port->bc; s = spltty(); open_top: while (port->status & DIGI_DTR_OFF) { port->wopeners++; error = tsleep(&port->dtr_wait, TTIPRI | PCATCH, "digidtr", 0); port->wopeners--; if (error) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!port->active_out) { error = EBUSY; DLOG(DIGIDB_OPEN, (sc->dev, "port %d:" " BUSY error = %d\n", pnum, error)); goto out; } } else if (port->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; DLOG(DIGIDB_OPEN, (sc->dev, "port %d: BUSY error = %d\n", pnum, error)); goto out; } port->wopeners++; error = tsleep(&port->active_out, TTIPRI | PCATCH, "digibi", 0); port->wopeners--; if (error != 0) { DLOG(DIGIDB_OPEN, (sc->dev, "port %d: tsleep(digibi) error = %d\n", pnum, error)); goto out; } goto open_top; } if (tp->t_state & TS_XCLUDE && suser(td) != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are callout, * and to complete a callin open after DCD rises. */ tp->t_oproc = digistart; tp->t_param = digiparam; tp->t_stop = digistop; tp->t_dev = dev; tp->t_termios = (mynor & CALLOUT_MASK) ? port->it_out : port->it_in; sc->setwin(sc, 0); bc->rout = bc->rin; /* clear input queue */ bc->idata = 1; bc->iempty = 1; bc->ilow = 1; bc->mint = port->cd | port->sc->csigs->ri; bc->tin = bc->tout; if (port->ialtpin) { port->cd = sc->csigs->dsr; port->dsr = sc->csigs->cd; } else { port->cd = sc->csigs->cd; port->dsr = sc->csigs->dsr; } port->wopeners++; /* XXX required ? */ error = digiparam(tp, &tp->t_termios); port->wopeners--; if (error != 0) { DLOG(DIGIDB_OPEN, (sc->dev, "port %d: cxpparam error = %d\n", pnum, error)); goto out; } ttsetwater(tp); /* handle fake and initial DCD for callout devices */ if (bc->mstat & port->cd || mynor & CALLOUT_MASK) ttyld_modem(tp, 1); } /* Wait for DCD if necessary */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { port->wopeners++; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "digidcd", 0); port->wopeners--; if (error != 0) { DLOG(DIGIDB_OPEN, (sc->dev, "port %d: tsleep(digidcd) error = %d\n", pnum, error)); goto out; } goto open_top; } error = ttyld_open(tp, dev); DLOG(DIGIDB_OPEN, (sc->dev, "port %d: l_open error = %d\n", pnum, error)); ttyldoptim(tp); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) port->active_out = TRUE; if (tp->t_state & TS_ISOPEN) sc->opencnt++; out: splx(s); if (!(tp->t_state & TS_ISOPEN)) digihardclose(port); DLOG(DIGIDB_OPEN, (sc->dev, "port %d: open() returns %d\n", pnum, error)); return (error); } static int digiclose(dev_t dev, int flag, int mode, struct thread *td) { int mynor; struct tty *tp; int unit, pnum; struct digi_softc *sc; struct digi_p *port; int s; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); pnum = MINOR_TO_PORT(mynor); sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); KASSERT(sc, ("digi%d: softc not allocated in digiclose\n", unit)); if (mynor & (CTRL_DEV | CONTROL_MASK)) { sc->opencnt--; return (0); } port = sc->ports + pnum; tp = port->tp; DLOG(DIGIDB_CLOSE, (sc->dev, "port %d: closing\n", pnum)); s = spltty(); ttyld_close(tp, flag); ttyldoptim(tp); digihardclose(port); ttyclose(tp); if (--sc->opencnt == 0) splx(s); return (0); } static void digidtrwakeup(void *chan) { struct digi_p *port = chan; port->status &= ~DIGI_DTR_OFF; wakeup(&port->dtr_wait); port->wopeners--; } static void digihardclose(struct digi_p *port) { volatile struct board_chan *bc; int s; bc = port->bc; s = spltty(); port->sc->setwin(port->sc, 0); bc->idata = 0; bc->iempty = 0; bc->ilow = 0; bc->mint = 0; if ((port->tp->t_cflag & HUPCL) || (!port->active_out && !(bc->mstat & port->cd) && !(port->it_in.c_cflag & CLOCAL)) || !(port->tp->t_state & TS_ISOPEN)) { digimctl(port, TIOCM_DTR | TIOCM_RTS, DMBIC); if (port->dtr_wait != 0) { /* Schedule a wakeup of any callin devices */ port->wopeners++; timeout(&digidtrwakeup, port, port->dtr_wait); port->status |= DIGI_DTR_OFF; } } port->active_out = FALSE; wakeup(&port->active_out); wakeup(TSA_CARR_ON(port->tp)); splx(s); } static int digiread(dev_t dev, struct uio *uio, int flag) { int mynor; struct tty *tp; int error, unit, pnum; struct digi_softc *sc; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); pnum = MINOR_TO_PORT(mynor); sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); KASSERT(sc, ("digi%d: softc not allocated in digiclose\n", unit)); tp = &sc->ttys[pnum]; error = ttyld_read(tp, uio, flag); DLOG(DIGIDB_READ, (sc->dev, "port %d: read() returns %d\n", pnum, error)); return (error); } static int digiwrite(dev_t dev, struct uio *uio, int flag) { int mynor; struct tty *tp; int error, unit, pnum; struct digi_softc *sc; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); pnum = MINOR_TO_PORT(mynor); sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); KASSERT(sc, ("digi%d: softc not allocated in digiclose\n", unit)); tp = &sc->ttys[pnum]; error = ttyld_write(tp, uio, flag); DLOG(DIGIDB_WRITE, (sc->dev, "port %d: write() returns %d\n", pnum, error)); return (error); } /* * Load module "digi_.ko" and look for a symbol called digi_mod_. * * Populate sc->bios, sc->fep, and sc->link from this data. * * sc->fep.data, sc->bios.data and sc->link.data are malloc()d according * to their respective sizes. * * The module is unloaded when we're done. */ static int digi_loadmoduledata(struct digi_softc *sc) { struct digi_mod *digi_mod; linker_file_t lf; char *modfile, *sym; caddr_t symptr; int modlen, res; KASSERT(sc->bios.data == NULL, ("Uninitialised BIOS variable")); KASSERT(sc->fep.data == NULL, ("Uninitialised FEP variable")); KASSERT(sc->link.data == NULL, ("Uninitialised LINK variable")); KASSERT(sc->module != NULL, ("Uninitialised module name")); modlen = strlen(sc->module); modfile = malloc(modlen + 6, M_TEMP, M_WAITOK); snprintf(modfile, modlen + 6, "digi_%s", sc->module); if ((res = linker_reference_module(modfile, NULL, &lf)) != 0) { if (res == ENOENT && rootdev == NODEV) printf("%s: Failed to autoload module: No filesystem\n", modfile); else printf("%s: Failed %d to autoload module\n", modfile, res); } free(modfile, M_TEMP); if (res != 0) return (res); sym = malloc(modlen + 10, M_TEMP, M_WAITOK); snprintf(sym, modlen + 10, "digi_mod_%s", sc->module); if ((symptr = linker_file_lookup_symbol(lf, sym, 0)) == NULL) printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, sym); free(sym, M_TEMP); digi_mod = (struct digi_mod *)symptr; if (digi_mod->dm_version != DIGI_MOD_VERSION) { printf("digi_%s.ko: Invalid version %d (need %d)\n", sc->module, digi_mod->dm_version, DIGI_MOD_VERSION); linker_file_unload(lf); return (EINVAL); } sc->bios.size = digi_mod->dm_bios.size; if (sc->bios.size != 0 && digi_mod->dm_bios.data != NULL) { sc->bios.data = malloc(sc->bios.size, M_TTYS, M_WAITOK); bcopy(digi_mod->dm_bios.data, sc->bios.data, sc->bios.size); } sc->fep.size = digi_mod->dm_fep.size; if (sc->fep.size != 0 && digi_mod->dm_fep.data != NULL) { sc->fep.data = malloc(sc->fep.size, M_TTYS, M_WAITOK); bcopy(digi_mod->dm_fep.data, sc->fep.data, sc->fep.size); } sc->link.size = digi_mod->dm_link.size; if (sc->link.size != 0 && digi_mod->dm_link.data != NULL) { sc->link.data = malloc(sc->link.size, M_TTYS, M_WAITOK); bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size); } linker_file_unload(lf); return (0); } static int digiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) { int unit, pnum, mynor, error, s; struct digi_softc *sc; struct digi_p *port; struct tty *tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) int oldcmd; struct termios term; #endif mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); pnum = MINOR_TO_PORT(mynor); sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); KASSERT(sc, ("digi%d: softc not allocated in digiioctl\n", unit)); if (sc->status == DIGI_STATUS_DISABLED) return (ENXIO); if (mynor & CTRL_DEV) { switch (cmd) { case DIGIIO_DEBUG: #ifdef DEBUG digi_debug = *(int *)data; return (0); #else device_printf(sc->dev, "DEBUG not defined\n"); return (ENXIO); #endif case DIGIIO_REINIT: digi_loadmoduledata(sc); error = digi_init(sc); digi_freemoduledata(sc); return (error); case DIGIIO_MODEL: *(enum digi_model *)data = sc->model; return (0); case DIGIIO_IDENT: return (copyout(sc->name, *(char **)data, strlen(sc->name) + 1)); } } if (pnum >= sc->numports) return (ENXIO); port = sc->ports + pnum; if (!(port->status & ENABLED)) return (ENXIO); tp = port->tp; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = (mynor & CALLOUT_MASK) ? &port->it_out : &port->it_in; break; case CONTROL_LOCK_STATE: ct = (mynor & CALLOUT_MASK) ? &port->lt_out : &port->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(td); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); case DIGIIO_GETALTPIN: switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: *(int *)data = port->ialtpin; break; case CONTROL_LOCK_STATE: *(int *)data = port->laltpin; break; default: panic("Confusion when re-testing minor"); return (ENODEV); } return (0); case DIGIIO_SETALTPIN: switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: if (!port->laltpin) { port->ialtpin = !!*(int *)data; DLOG(DIGIDB_SET, (sc->dev, "port%d: initial ALTPIN %s\n", pnum, port->ialtpin ? "set" : "cleared")); } break; case CONTROL_LOCK_STATE: port->laltpin = !!*(int *)data; DLOG(DIGIDB_SET, (sc->dev, "port%d: ALTPIN %slocked\n", pnum, port->laltpin ? "" : "un")); break; default: panic("Confusion when re-testing minor"); return (ENODEV); } return (0); default: return (ENOTTY); } } switch (cmd) { case DIGIIO_GETALTPIN: *(int *)data = !!(port->dsr == sc->csigs->cd); return (0); case DIGIIO_SETALTPIN: if (!port->laltpin) { if (*(int *)data) { DLOG(DIGIDB_SET, (sc->dev, "port%d: ALTPIN set\n", pnum)); port->cd = sc->csigs->dsr; port->dsr = sc->csigs->cd; } else { DLOG(DIGIDB_SET, (sc->dev, "port%d: ALTPIN cleared\n", pnum)); port->cd = sc->csigs->cd; port->dsr = sc->csigs->dsr; } } return (0); } tp = port->tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t) & term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt; struct termios *lt; dt = (struct termios *)data; lt = (mynor & CALLOUT_MASK) ? &port->lt_out : &port->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); port->c_iflag = dt->c_iflag & (IXOFF | IXON | IXANY); dt->c_iflag &= ~(IXOFF | IXON | IXANY); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = ttyioctl(dev, cmd, data, flag, td); if (error == 0 && cmd == TIOCGETA) ((struct termios *)data)->c_iflag |= port->c_iflag; ttyldoptim(tp); if (error >= 0 && error != ENOTTY) return (error); s = spltty(); sc->setwin(sc, 0); switch (cmd) { case DIGIIO_RING: port->send_ring = *(u_char *)data; break; case TIOCSBRK: /* * now it sends 400 millisecond break because I don't know * how to send an infinite break */ fepcmd_w(port, SENDBREAK, 400, 10); break; case TIOCCBRK: /* now it's empty */ break; case TIOCSDTR: digimctl(port, TIOCM_DTR, DMBIS); break; case TIOCCDTR: digimctl(port, TIOCM_DTR, DMBIC); break; case TIOCMSET: digimctl(port, *(int *)data, DMSET); break; case TIOCMBIS: digimctl(port, *(int *)data, DMBIS); break; case TIOCMBIC: digimctl(port, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = digimctl(port, 0, DMGET); break; case TIOCMSDTRWAIT: error = suser(td); if (error != 0) { splx(s); return (error); } port->dtr_wait = *(int *)data *hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = port->dtr_wait * 100 / hz; break; #ifdef DIGI_INTERRUPT case TIOCTIMESTAMP: *(struct timeval *)data = sc->intr_timestamp; break; #endif default: splx(s); return (ENOTTY); } splx(s); return (0); } static int digiparam(struct tty *tp, struct termios *t) { int mynor; int unit; int pnum; struct digi_softc *sc; struct digi_p *port; int cflag; int iflag; int hflow; int s; int window; mynor = minor(tp->t_dev); unit = MINOR_TO_UNIT(mynor); pnum = MINOR_TO_PORT(mynor); sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); KASSERT(sc, ("digi%d: softc not allocated in digiparam\n", unit)); port = &sc->ports[pnum]; DLOG(DIGIDB_SET, (sc->dev, "port%d: setting parameters\n", pnum)); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; cflag = ttspeedtab(t->c_ospeed, digispeedtab); if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed)) return (EINVAL); s = splclock(); window = sc->window; sc->setwin(sc, 0); if (cflag == 0) { /* hangup */ DLOG(DIGIDB_SET, (sc->dev, "port%d: hangup\n", pnum)); digimctl(port, TIOCM_DTR | TIOCM_RTS, DMBIC); } else { digimctl(port, TIOCM_DTR | TIOCM_RTS, DMBIS); DLOG(DIGIDB_SET, (sc->dev, "port%d: CBAUD = %d\n", pnum, cflag)); #if 0 /* convert flags to sysV-style values */ if (t->c_cflag & PARODD) cflag |= 0x0200; if (t->c_cflag & PARENB) cflag |= 0x0100; if (t->c_cflag & CSTOPB) cflag |= 0x0080; #else /* convert flags to sysV-style values */ if (t->c_cflag & PARODD) cflag |= FEP_PARODD; if (t->c_cflag & PARENB) cflag |= FEP_PARENB; if (t->c_cflag & CSTOPB) cflag |= FEP_CSTOPB; if (t->c_cflag & CLOCAL) cflag |= FEP_CLOCAL; #endif cflag |= (t->c_cflag & CSIZE) >> 4; DLOG(DIGIDB_SET, (sc->dev, "port%d: CFLAG = 0x%x\n", pnum, cflag)); fepcmd_w(port, SETCFLAGS, (unsigned)cflag, 0); } iflag = t->c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP); if (port->c_iflag & IXON) iflag |= 0x400; if (port->c_iflag & IXANY) iflag |= 0x800; if (port->c_iflag & IXOFF) iflag |= 0x1000; DLOG(DIGIDB_SET, (sc->dev, "port%d: set iflag = 0x%x\n", pnum, iflag)); fepcmd_w(port, SETIFLAGS, (unsigned)iflag, 0); hflow = 0; if (t->c_cflag & CDTR_IFLOW) hflow |= sc->csigs->dtr; if (t->c_cflag & CRTS_IFLOW) hflow |= sc->csigs->rts; if (t->c_cflag & CCTS_OFLOW) hflow |= sc->csigs->cts; if (t->c_cflag & CDSR_OFLOW) hflow |= port->dsr; if (t->c_cflag & CCAR_OFLOW) hflow |= port->cd; DLOG(DIGIDB_SET, (sc->dev, "port%d: set hflow = 0x%x\n", pnum, hflow)); fepcmd_w(port, SETHFLOW, 0xff00 | (unsigned)hflow, 0); DLOG(DIGIDB_SET, (sc->dev, "port%d: set startc(0x%x), stopc(0x%x)\n", pnum, t->c_cc[VSTART], t->c_cc[VSTOP])); fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0); if (sc->window != 0) sc->towin(sc, 0); if (window != 0) sc->towin(sc, window); splx(s); return (0); } static void digi_intr(void *vp) { struct digi_p *port; char *cxcon; struct digi_softc *sc; int ehead, etail; volatile struct board_chan *bc; struct tty *tp; int head, tail; int wrapmask; int size, window; struct event { u_char pnum; u_char event; u_char mstat; u_char lstat; } event; sc = vp; if (sc->status != DIGI_STATUS_ENABLED) { DLOG(DIGIDB_IRQ, (sc->dev, "interrupt on disabled board !\n")); return; } #ifdef DIGI_INTERRUPT microtime(&sc->intr_timestamp); #endif window = sc->window; sc->setwin(sc, 0); if (sc->model >= PCXEM && W(sc->vmem + 0xd00)) { struct con_bios *con = con_bios_list; register u_char *ptr; ptr = sc->vmem + W(sc->vmem + 0xd00); while (con) { if (ptr[1] && W(ptr + 2) == W(con->bios + 2)) /* Not first block -- exact match */ break; if (W(ptr + 4) >= W(con->bios + 4) && W(ptr + 4) <= W(con->bios + 6)) /* Initial search concetrator BIOS */ break; } if (con == NULL) { log(LOG_ERR, "digi%d: wanted bios LREV = 0x%04x" " not found!\n", sc->res.unit, W(ptr + 4)); W(ptr + 10) = 0; W(sc->vmem + 0xd00) = 0; goto eoi; } cxcon = con->bios; W(ptr + 4) = W(cxcon + 4); W(ptr + 6) = W(cxcon + 6); if (ptr[1] == 0) W(ptr + 2) = W(cxcon + 2); W(ptr + 8) = (ptr[1] << 6) + W(cxcon + 8); size = W(cxcon + 10) - (ptr[1] << 10); if (size <= 0) { W(ptr + 8) = W(cxcon + 8); W(ptr + 10) = 0; } else { if (size > 1024) size = 1024; W(ptr + 10) = size; bcopy(cxcon + (ptr[1] << 10), ptr + 12, size); } W(sc->vmem + 0xd00) = 0; goto eoi; } ehead = sc->gdata->ein; etail = sc->gdata->eout; if (ehead == etail) { #ifdef DEBUG sc->intr_count++; if (sc->intr_count % 6000 == 0) { DLOG(DIGIDB_IRQ, (sc->dev, "6000 useless polls %x %x\n", ehead, etail)); sc->intr_count = 0; } #endif goto eoi; } while (ehead != etail) { event = *(volatile struct event *)(sc->memevent + etail); etail = (etail + 4) & sc->gdata->imax; if (event.pnum >= sc->numports) { log(LOG_ERR, "digi%d: port %d: got event" " on nonexisting port\n", sc->res.unit, event.pnum); continue; } port = &sc->ports[event.pnum]; bc = port->bc; tp = port->tp; if (!(tp->t_state & TS_ISOPEN) && !port->wopeners) { DLOG(DIGIDB_IRQ, (sc->dev, "port %d: event 0x%x on closed port\n", event.pnum, event.event)); bc->rout = bc->rin; bc->idata = 0; bc->iempty = 0; bc->ilow = 0; bc->mint = 0; continue; } if (event.event & ~ALL_IND) log(LOG_ERR, "digi%d: port%d: ? event 0x%x mstat 0x%x" " lstat 0x%x\n", sc->res.unit, event.pnum, event.event, event.mstat, event.lstat); if (event.event & DATA_IND) { DLOG(DIGIDB_IRQ, (sc->dev, "port %d: DATA_IND\n", event.pnum)); wrapmask = port->rxbufsize - 1; head = bc->rin; tail = bc->rout; size = 0; if (!(tp->t_state & TS_ISOPEN)) { bc->rout = head; goto end_of_data; } while (head != tail) { int top; DLOG(DIGIDB_INT, (sc->dev, "port %d: p rx head = %d tail = %d\n", event.pnum, head, tail)); top = (head > tail) ? head : wrapmask + 1; sc->towin(sc, port->rxwin); size = top - tail; if (tp->t_state & TS_CAN_BYPASS_L_RINT) { size = b_to_q((char *)port->rxbuf + tail, size, &tp->t_rawq); tail = top - size; ttwakeup(tp); } else for (; tail < top;) { ttyld_rint(tp, port->rxbuf[tail]); sc->towin(sc, port->rxwin); size--; tail++; if (tp->t_state & TS_TBLOCK) break; } tail &= wrapmask; sc->setwin(sc, 0); bc->rout = tail; head = bc->rin; if (size) break; } if (bc->orun) { CE_RECORD(port, CE_OVERRUN); log(LOG_ERR, "digi%d: port%d: %s\n", sc->res.unit, event.pnum, digi_errortxt(CE_OVERRUN)); bc->orun = 0; } end_of_data: if (size) { tp->t_state |= TS_TBLOCK; port->status |= PAUSE_RX; DLOG(DIGIDB_RX, (sc->dev, "port %d: pause RX\n", event.pnum)); } else { bc->idata = 1; } } if (event.event & MODEMCHG_IND) { DLOG(DIGIDB_MODEM, (sc->dev, "port %d: MODEMCHG_IND\n", event.pnum)); if ((event.mstat ^ event.lstat) & port->cd) { sc->hidewin(sc); ttyld_modem(tp, event.mstat & port->cd); sc->setwin(sc, 0); wakeup(TSA_CARR_ON(tp)); } if (event.mstat & sc->csigs->ri) { DLOG(DIGIDB_RI, (sc->dev, "port %d: RING\n", event.pnum)); if (port->send_ring) { ttyld_rint(tp, 'R'); ttyld_rint(tp, 'I'); ttyld_rint(tp, 'N'); ttyld_rint(tp, 'G'); ttyld_rint(tp, '\r'); ttyld_rint(tp, '\n'); } } } if (event.event & BREAK_IND) { DLOG(DIGIDB_MODEM, (sc->dev, "port %d: BREAK_IND\n", event.pnum)); ttyld_rint(tp, TTY_BI); } if (event.event & (LOWTX_IND | EMPTYTX_IND)) { DLOG(DIGIDB_IRQ, (sc->dev, "port %d:%s%s\n", event.pnum, event.event & LOWTX_IND ? " LOWTX" : "", event.event & EMPTYTX_IND ? " EMPTYTX" : "")); ttyld_start(tp); } } sc->gdata->eout = etail; eoi: if (sc->window != 0) sc->towin(sc, 0); if (window != 0) sc->towin(sc, window); } static void digistart(struct tty *tp) { int unit; int pnum; struct digi_p *port; struct digi_softc *sc; volatile struct board_chan *bc; int head, tail; int size, ocount, totcnt = 0; int s; int wmask; unit = MINOR_TO_UNIT(minor(tp->t_dev)); pnum = MINOR_TO_PORT(minor(tp->t_dev)); sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); KASSERT(sc, ("digi%d: softc not allocated in digistart\n", unit)); port = &sc->ports[pnum]; bc = port->bc; wmask = port->txbufsize - 1; s = spltty(); port->lcc = tp->t_outq.c_cc; sc->setwin(sc, 0); if (!(tp->t_state & TS_TBLOCK)) { if (port->status & PAUSE_RX) { DLOG(DIGIDB_RX, (sc->dev, "port %d: resume RX\n", pnum)); /* * CAREFUL - braces are needed here if the DLOG is * optimised out! */ } port->status &= ~PAUSE_RX; bc->idata = 1; } if (!(tp->t_state & TS_TTSTOP) && port->status & PAUSE_TX) { DLOG(DIGIDB_TX, (sc->dev, "port %d: resume TX\n", pnum)); port->status &= ~PAUSE_TX; fepcmd_w(port, RESUMETX, 0, 10); } if (tp->t_outq.c_cc == 0) tp->t_state &= ~TS_BUSY; else tp->t_state |= TS_BUSY; head = bc->tin; while (tp->t_outq.c_cc != 0) { tail = bc->tout; DLOG(DIGIDB_INT, (sc->dev, "port%d: s tx head = %d tail = %d\n", pnum, head, tail)); if (head < tail) size = tail - head - 1; else { size = port->txbufsize - head; if (tail == 0) size--; } if (size == 0) break; sc->towin(sc, port->txwin); ocount = q_to_b(&tp->t_outq, port->txbuf + head, size); totcnt += ocount; head += ocount; head &= wmask; sc->setwin(sc, 0); bc->tin = head; bc->iempty = 1; bc->ilow = 1; } port->lostcc = tp->t_outq.c_cc; tail = bc->tout; if (head < tail) size = port->txbufsize - tail + head; else size = head - tail; port->lbuf = size; DLOG(DIGIDB_INT, (sc->dev, "port%d: s total cnt = %d\n", pnum, totcnt)); ttwwakeup(tp); splx(s); } static void digistop(struct tty *tp, int rw) { struct digi_softc *sc; int unit; int pnum; struct digi_p *port; unit = MINOR_TO_UNIT(minor(tp->t_dev)); pnum = MINOR_TO_PORT(minor(tp->t_dev)); sc = (struct digi_softc *)devclass_get_softc(digi_devclass, unit); KASSERT(sc, ("digi%d: softc not allocated in digistop\n", unit)); port = sc->ports + pnum; DLOG(DIGIDB_TX, (sc->dev, "port %d: pause TX\n", pnum)); port->status |= PAUSE_TX; fepcmd_w(port, PAUSETX, 0, 10); } static void fepcmd(struct digi_p *port, int cmd, int op1, int ncmds) { u_char *mem; unsigned tail, head; int count, n; mem = port->sc->memcmd; port->sc->setwin(port->sc, 0); head = port->sc->gdata->cin; mem[head + 0] = cmd; mem[head + 1] = port->pnum; *(u_short *)(mem + head + 2) = op1; head = (head + 4) & port->sc->gdata->cmax; port->sc->gdata->cin = head; for (count = FEPTIMEOUT; count > 0; count--) { head = port->sc->gdata->cin; tail = port->sc->gdata->cout; n = (head - tail) & port->sc->gdata->cmax; if (n <= ncmds * sizeof(short) * 4) break; } if (count == 0) log(LOG_ERR, "digi%d: port%d: timeout on FEP command\n", port->sc->res.unit, port->pnum); } const char * digi_errortxt(int id) { static const char *error_desc[] = { "silo overflow", "interrupt-level buffer overflow", "tty-level buffer overflow", }; KASSERT(id >= 0 && id < sizeof(error_desc) / sizeof(error_desc[0]), ("Unexpected digi error id %d\n", id)); return (error_desc[id]); } int digi_attach(struct digi_softc *sc) { sc->res.ctldev = make_dev(&digi_sw, (sc->res.unit << 16) | CTRL_DEV, UID_ROOT, GID_WHEEL, 0600, "digi%r.ctl", sc->res.unit); digi_loadmoduledata(sc); digi_init(sc); digi_freemoduledata(sc); return (0); } static int digi_inuse(struct digi_softc *sc) { int i; for (i = 0; i < sc->numports; i++) if (sc->ttys[i].t_state & TS_ISOPEN) { DLOG(DIGIDB_INIT, (sc->dev, "port%d: busy\n", i)); return (1); } else if (sc->ports[i].wopeners || sc->ports[i].opencnt) { DLOG(DIGIDB_INIT, (sc->dev, "port%d: blocked in open\n", i)); return (1); } return (0); } static void digi_free_state(struct digi_softc *sc) { int d, i; /* Blow it all away */ for (i = 0; i < sc->numports; i++) for (d = 0; d < 6; d++) destroy_dev(sc->ports[i].dev[d]); untimeout(digi_poll, sc, sc->callout); callout_handle_init(&sc->callout); untimeout(digi_int_test, sc, sc->inttest); callout_handle_init(&sc->inttest); bus_teardown_intr(sc->dev, sc->res.irq, sc->res.irqHandler); #ifdef DIGI_INTERRUPT if (sc->res.irq != NULL) { bus_release_resource(dev, SYS_RES_IRQ, sc->res.irqrid, sc->res.irq); sc->res.irq = NULL; } #endif if (sc->numports) { KASSERT(sc->ports, ("digi%d: Lost my ports ?", sc->res.unit)); KASSERT(sc->ttys, ("digi%d: Lost my ttys ?", sc->res.unit)); free(sc->ports, M_TTYS); sc->ports = NULL; free(sc->ttys, M_TTYS); sc->ttys = NULL; sc->numports = 0; } sc->status = DIGI_STATUS_NOTINIT; } int digi_detach(device_t dev) { struct digi_softc *sc = device_get_softc(dev); DLOG(DIGIDB_INIT, (sc->dev, "detaching\n")); /* If we're INIT'd, numports must be 0 */ KASSERT(sc->numports == 0 || sc->status != DIGI_STATUS_NOTINIT, ("digi%d: numports(%d) & status(%d) are out of sync", sc->res.unit, sc->numports, (int)sc->status)); if (digi_inuse(sc)) return (EBUSY); digi_free_state(sc); destroy_dev(sc->res.ctldev); if (sc->res.mem != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid, sc->res.mem); sc->res.mem = NULL; } if (sc->res.io != NULL) { bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid, sc->res.io); sc->res.io = NULL; } return (0); } int digi_shutdown(device_t dev) { return (0); } MODULE_VERSION(digi, 1); Index: head/sys/dev/rp/rp.c =================================================================== --- head/sys/dev/rp/rp.c (revision 130343) +++ head/sys/dev/rp/rp.c (revision 130344) @@ -1,1657 +1,1657 @@ /* * Copyright (c) Comtrol Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted prodived that the follwoing conditions * are met. * 1. Redistributions of source code must retain the above copyright * notive, this list of conditions and the following disclainer. * 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 prodided 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 Comtrol Corporation. * 4. The name of Comtrol Corporation may not be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``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 COMTROL CORPORATION 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, LIFE 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. * */ #include __FBSDID("$FreeBSD$"); /* * rp.c - for RocketPort FreeBSD */ #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #define ROCKET_C #include #include static const char RocketPortVersion[] = "3.02"; static Byte_t RData[RDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, 0x02, 0x09, 0x86, 0xfb, 0x04, 0x09, 0x00, 0x0a, 0x06, 0x09, 0x01, 0x0a, 0x08, 0x09, 0x8a, 0x13, 0x0a, 0x09, 0xc5, 0x11, 0x0c, 0x09, 0x86, 0x85, 0x0e, 0x09, 0x20, 0x0a, 0x10, 0x09, 0x21, 0x0a, 0x12, 0x09, 0x41, 0xff, 0x14, 0x09, 0x82, 0x00, 0x16, 0x09, 0x82, 0x7b, 0x18, 0x09, 0x8a, 0x7d, 0x1a, 0x09, 0x88, 0x81, 0x1c, 0x09, 0x86, 0x7a, 0x1e, 0x09, 0x84, 0x81, 0x20, 0x09, 0x82, 0x7c, 0x22, 0x09, 0x0a, 0x0a }; static Byte_t RRegData[RREGDATASIZE]= { 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ }; #if 0 /* IRQ number to MUDBAC register 2 mapping */ Byte_t sIRQMap[16] = { 0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80 }; #endif Byte_t rp_sBitMapClrTbl[8] = { 0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f }; Byte_t rp_sBitMapSetTbl[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }; /* Actually not used */ #if notdef struct termios deftermios = { TTYDEF_IFLAG, TTYDEF_OFLAG, TTYDEF_CFLAG, TTYDEF_LFLAG, { CEOF, CEOL, CEOL, CERASE, CWERASE, CKILL, CREPRINT, _POSIX_VDISABLE, CINTR, CQUIT, CSUSP, CDSUSP, CSTART, CSTOP, CLNEXT, CDISCARD, CMIN, CTIME, CSTATUS, _POSIX_VDISABLE }, TTYDEF_SPEED, TTYDEF_SPEED }; #endif /*************************************************************************** Function: sReadAiopID Purpose: Read the AIOP idenfication number directly from an AIOP. Call: sReadAiopID(CtlP, aiop) CONTROLLER_T *CtlP; Ptr to controller structure int aiop: AIOP index Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X is replace by an identifying number. Flag AIOPID_NULL if no valid AIOP is found Warnings: No context switches are allowed while executing this function. */ int sReadAiopID(CONTROLLER_T *CtlP, int aiop) { Byte_t AiopID; /* ID byte from AIOP */ rp_writeaiop1(CtlP, aiop, _CMD_REG, RESET_ALL); /* reset AIOP */ rp_writeaiop1(CtlP, aiop, _CMD_REG, 0x0); AiopID = rp_readaiop1(CtlP, aiop, _CHN_STAT0) & 0x07; if(AiopID == 0x06) return(1); else /* AIOP does not exist */ return(-1); } /*************************************************************************** Function: sReadAiopNumChan Purpose: Read the number of channels available in an AIOP directly from an AIOP. Call: sReadAiopNumChan(CtlP, aiop) CONTROLLER_T *CtlP; Ptr to controller structure int aiop: AIOP index Return: int: The number of channels available Comments: The number of channels is determined by write/reads from identical offsets within the SRAM address spaces for channels 0 and 4. If the channel 4 space is mirrored to channel 0 it is a 4 channel AIOP, otherwise it is an 8 channel. Warnings: No context switches are allowed while executing this function. */ int sReadAiopNumChan(CONTROLLER_T *CtlP, int aiop) { Word_t x, y; rp_writeaiop4(CtlP, aiop, _INDX_ADDR,0x12340000L); /* write to chan 0 SRAM */ rp_writeaiop2(CtlP, aiop, _INDX_ADDR,0); /* read from SRAM, chan 0 */ x = rp_readaiop2(CtlP, aiop, _INDX_DATA); rp_writeaiop2(CtlP, aiop, _INDX_ADDR,0x4000); /* read from SRAM, chan 4 */ y = rp_readaiop2(CtlP, aiop, _INDX_DATA); if(x != y) /* if different must be 8 chan */ return(8); else return(4); } /*************************************************************************** Function: sInitChan Purpose: Initialization of a channel and channel structure Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) CONTROLLER_T *CtlP; Ptr to controller structure CHANNEL_T *ChP; Ptr to channel structure int AiopNum; AIOP number within controller int ChanNum; Channel number within AIOP Return: int: TRUE if initialization succeeded, FALSE if it fails because channel number exceeds number of channels available in AIOP. Comments: This function must be called before a channel can be used. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. */ int sInitChan( CONTROLLER_T *CtlP, CHANNEL_T *ChP, int AiopNum, int ChanNum) { int i, ChOff; Byte_t *ChR; static Byte_t R[4]; if(ChanNum >= CtlP->AiopNumChan[AiopNum]) return(FALSE); /* exceeds num chans in AIOP */ /* Channel, AIOP, and controller identifiers */ ChP->CtlP = CtlP; ChP->ChanID = CtlP->AiopID[AiopNum]; ChP->AiopNum = AiopNum; ChP->ChanNum = ChanNum; /* Initialize the channel from the RData array */ for(i=0; i < RDATASIZE; i+=4) { R[0] = RData[i]; R[1] = RData[i+1] + 0x10 * ChanNum; R[2] = RData[i+2]; R[3] = RData[i+3]; rp_writech4(ChP,_INDX_ADDR,*((DWord_t *)&R[0])); } ChR = ChP->R; for(i=0; i < RREGDATASIZE; i+=4) { ChR[i] = RRegData[i]; ChR[i+1] = RRegData[i+1] + 0x10 * ChanNum; ChR[i+2] = RRegData[i+2]; ChR[i+3] = RRegData[i+3]; } /* Indexed registers */ ChOff = (Word_t)ChanNum * 0x1000; ChP->BaudDiv[0] = (Byte_t)(ChOff + _BAUD); ChP->BaudDiv[1] = (Byte_t)((ChOff + _BAUD) >> 8); ChP->BaudDiv[2] = (Byte_t)BRD9600; ChP->BaudDiv[3] = (Byte_t)(BRD9600 >> 8); rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->BaudDiv[0]); ChP->TxControl[0] = (Byte_t)(ChOff + _TX_CTRL); ChP->TxControl[1] = (Byte_t)((ChOff + _TX_CTRL) >> 8); ChP->TxControl[2] = 0; ChP->TxControl[3] = 0; rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->TxControl[0]); ChP->RxControl[0] = (Byte_t)(ChOff + _RX_CTRL); ChP->RxControl[1] = (Byte_t)((ChOff + _RX_CTRL) >> 8); ChP->RxControl[2] = 0; ChP->RxControl[3] = 0; rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->RxControl[0]); ChP->TxEnables[0] = (Byte_t)(ChOff + _TX_ENBLS); ChP->TxEnables[1] = (Byte_t)((ChOff + _TX_ENBLS) >> 8); ChP->TxEnables[2] = 0; ChP->TxEnables[3] = 0; rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->TxEnables[0]); ChP->TxCompare[0] = (Byte_t)(ChOff + _TXCMP1); ChP->TxCompare[1] = (Byte_t)((ChOff + _TXCMP1) >> 8); ChP->TxCompare[2] = 0; ChP->TxCompare[3] = 0; rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->TxCompare[0]); ChP->TxReplace1[0] = (Byte_t)(ChOff + _TXREP1B1); ChP->TxReplace1[1] = (Byte_t)((ChOff + _TXREP1B1) >> 8); ChP->TxReplace1[2] = 0; ChP->TxReplace1[3] = 0; rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->TxReplace1[0]); ChP->TxReplace2[0] = (Byte_t)(ChOff + _TXREP2); ChP->TxReplace2[1] = (Byte_t)((ChOff + _TXREP2) >> 8); ChP->TxReplace2[2] = 0; ChP->TxReplace2[3] = 0; rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->TxReplace2[0]); ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; ChP->TxFIFO = ChOff + _TX_FIFO; rp_writech1(ChP,_CMD_REG,(Byte_t)ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ rp_writech1(ChP,_CMD_REG,(Byte_t)ChanNum); /* remove reset Tx FIFO count */ rp_writech2(ChP,_INDX_ADDR,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ rp_writech2(ChP,_INDX_DATA,0); ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; ChP->RxFIFO = ChOff + _RX_FIFO; rp_writech1(ChP,_CMD_REG,(Byte_t)ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ rp_writech1(ChP,_CMD_REG,(Byte_t)ChanNum); /* remove reset Rx FIFO count */ rp_writech2(ChP,_INDX_ADDR,ChP->RxFIFOPtrs); /* clear Rx out ptr */ rp_writech2(ChP,_INDX_DATA,0); rp_writech2(ChP,_INDX_ADDR,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ rp_writech2(ChP,_INDX_DATA,0); ChP->TxPrioCnt = ChOff + _TXP_CNT; rp_writech2(ChP,_INDX_ADDR,ChP->TxPrioCnt); rp_writech1(ChP,_INDX_DATA,0); ChP->TxPrioPtr = ChOff + _TXP_PNTR; rp_writech2(ChP,_INDX_ADDR,ChP->TxPrioPtr); rp_writech1(ChP,_INDX_DATA,0); ChP->TxPrioBuf = ChOff + _TXP_BUF; sEnRxProcessor(ChP); /* start the Rx processor */ return(TRUE); } /*************************************************************************** Function: sStopRxProcessor Purpose: Stop the receive processor from processing a channel. Call: sStopRxProcessor(ChP) CHANNEL_T *ChP; Ptr to channel structure Comments: The receive processor can be started again with sStartRxProcessor(). This function causes the receive processor to skip over the stopped channel. It does not stop it from processing other channels. Warnings: No context switches are allowed while executing this function. Do not leave the receive processor stopped for more than one character time. After calling this function a delay of 4 uS is required to ensure that the receive processor is no longer processing this channel. */ void sStopRxProcessor(CHANNEL_T *ChP) { Byte_t R[4]; R[0] = ChP->R[0]; R[1] = ChP->R[1]; R[2] = 0x0a; R[3] = ChP->R[3]; rp_writech4(ChP, _INDX_ADDR,*(DWord_t *)&R[0]); } /*************************************************************************** Function: sFlushRxFIFO Purpose: Flush the Rx FIFO Call: sFlushRxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushRxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int RxFIFOEnabled; /* TRUE if Rx FIFO enabled */ if(sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ return; /* don't need to flush */ RxFIFOEnabled = FALSE; if(ChP->R[0x32] == 0x08) /* Rx FIFO is enabled */ { RxFIFOEnabled = TRUE; sDisRxFIFO(ChP); /* disable it */ for(i=0; i < 2000/200; i++) /* delay 2 uS to allow proc to disable FIFO*/ rp_readch1(ChP,_INT_CHAN); /* depends on bus i/o timing */ } sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ Ch = (Byte_t)sGetChanNum(ChP); rp_writech1(ChP,_CMD_REG,Ch | RESRXFCNT); /* apply reset Rx FIFO count */ rp_writech1(ChP,_CMD_REG,Ch); /* remove reset Rx FIFO count */ rp_writech2(ChP,_INDX_ADDR,ChP->RxFIFOPtrs); /* clear Rx out ptr */ rp_writech2(ChP,_INDX_DATA,0); rp_writech2(ChP,_INDX_ADDR,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ rp_writech2(ChP,_INDX_DATA,0); if(RxFIFOEnabled) sEnRxFIFO(ChP); /* enable Rx FIFO */ } /*************************************************************************** Function: sFlushTxFIFO Purpose: Flush the Tx FIFO Call: sFlushTxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushTxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int TxEnabled; /* TRUE if transmitter enabled */ if(sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ return; /* don't need to flush */ TxEnabled = FALSE; if(ChP->TxControl[3] & TX_ENABLE) { TxEnabled = TRUE; sDisTransmit(ChP); /* disable transmitter */ } sStopRxProcessor(ChP); /* stop Rx processor */ for(i = 0; i < 4000/200; i++) /* delay 4 uS to allow proc to stop */ rp_readch1(ChP,_INT_CHAN); /* depends on bus i/o timing */ Ch = (Byte_t)sGetChanNum(ChP); rp_writech1(ChP,_CMD_REG,Ch | RESTXFCNT); /* apply reset Tx FIFO count */ rp_writech1(ChP,_CMD_REG,Ch); /* remove reset Tx FIFO count */ rp_writech2(ChP,_INDX_ADDR,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ rp_writech2(ChP,_INDX_DATA,0); if(TxEnabled) sEnTransmit(ChP); /* enable transmitter */ sStartRxProcessor(ChP); /* restart Rx processor */ } /*************************************************************************** Function: sWriteTxPrioByte Purpose: Write a byte of priority transmit data to a channel Call: sWriteTxPrioByte(ChP,Data) CHANNEL_T *ChP; Ptr to channel structure Byte_t Data; The transmit data byte Return: int: 1 if the bytes is successfully written, otherwise 0. Comments: The priority byte is transmitted before any data in the Tx FIFO. Warnings: No context switches are allowed while executing this function. */ int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data) { Byte_t DWBuf[4]; /* buffer for double word writes */ Word_t *WordPtr; /* must be far because Win SS != DS */ if(sGetTxCnt(ChP) > 1) /* write it to Tx priority buffer */ { rp_writech2(ChP,_INDX_ADDR,ChP->TxPrioCnt); /* get priority buffer status */ if(rp_readch1(ChP,_INDX_DATA) & PRI_PEND) /* priority buffer busy */ return(0); /* nothing sent */ WordPtr = (Word_t *)(&DWBuf[0]); *WordPtr = ChP->TxPrioBuf; /* data byte address */ DWBuf[2] = Data; /* data byte value */ rp_writech4(ChP,_INDX_ADDR,*((DWord_t *)(&DWBuf[0]))); /* write it out */ *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ DWBuf[3] = 0; /* priority buffer pointer */ rp_writech4(ChP,_INDX_ADDR,*((DWord_t *)(&DWBuf[0]))); /* write it out */ } else /* write it to Tx FIFO */ { sWriteTxByte(ChP,sGetTxRxDataIO(ChP),Data); } return(1); /* 1 byte sent */ } /*************************************************************************** Function: sEnInterrupts Purpose: Enable one or more interrupts for a channel Call: sEnInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt enable flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Allow channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt enable flag is set in Flags, that interrupt will be enabled. If an interrupt enable flag is not set in Flags, that interrupt will not be changed. Interrupts can be disabled with function sDisInterrupts(). This function sets the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This allows this channel's bit to be set in the AIOP's Interrupt Channel Register. Interrupts must also be globally enabled before channel interrupts will be passed on to the host. This is done with function sEnGlobalInt(). In some cases it may be desirable to disable interrupts globally but enable channel interrupts. This would allow the global interrupt status register to be used to determine which AIOPs need service. */ void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] |= ((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] |= ((Byte_t)Flags & TXINT_EN); rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = rp_readch1(ChP,_INT_MASK) | rp_sBitMapSetTbl[ChP->ChanNum]; rp_writech1(ChP,_INT_MASK,Mask); } } /*************************************************************************** Function: sDisInterrupts Purpose: Disable one or more interrupts for a channel Call: sDisInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Disable channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt flag is set in Flags, that interrupt will be disabled. If an interrupt flag is not set in Flags, that interrupt will not be changed. Interrupts can be enabled with function sEnInterrupts(). This function clears the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This blocks this channel's bit from being set in the AIOP's Interrupt Channel Register. */ void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] &= ~((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] &= ~((Byte_t)Flags & TXINT_EN); rp_writech4(ChP,_INDX_ADDR,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = rp_readch1(ChP,_INT_MASK) & rp_sBitMapClrTbl[ChP->ChanNum]; rp_writech1(ChP,_INT_MASK,Mask); } } /********************************************************************* Begin FreeBsd-specific driver code **********************************************************************/ static timeout_t rpdtrwakeup; static d_open_t rpopen; static d_close_t rpclose; static d_write_t rpwrite; static d_ioctl_t rpioctl; struct cdevsw rp_cdevsw = { .d_version = D_VERSION, .d_open = rpopen, .d_close = rpclose, .d_write = rpwrite, .d_ioctl = rpioctl, .d_name = "rp", .d_flags = D_TTY | D_NEEDGIANT, }; static int rp_num_ports_open = 0; static int rp_ndevs = 0; static int minor_to_unit[128]; static int rp_num_ports[4]; /* Number of ports on each controller */ #define POLL_INTERVAL 1 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_UNIT(dev) (MINOR_TO_UNIT(minor(dev)) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_MAGIC(dev) ((minor(dev)) & ~MINOR_MAGIC_MASK) #define IS_CALLOUT(dev) (minor(dev) & CALLOUT_MASK) #define IS_CONTROL(dev) (minor(dev) & CONTROL_MASK) #define RP_ISMULTIPORT(dev) ((dev)->id_flags & 0x1) #define RP_MPMASTER(dev) (((dev)->id_flags >> 8) & 0xff) #define RP_NOTAST4(dev) ((dev)->id_flags & 0x04) static struct rp_port *p_rp_addr[4]; static struct rp_port *p_rp_table[MAX_RP_PORTS]; #define rp_addr(unit) (p_rp_addr[unit]) #define rp_table(port) (p_rp_table[port]) /* * The top-level routines begin here */ static int rpparam(struct tty *, struct termios *); static void rpstart(struct tty *); static void rpstop(struct tty *, int); static void rphardclose (struct rp_port *); static void rp_do_receive(struct rp_port *rp, struct tty *tp, CHANNEL_t *cp, unsigned int ChanStatus) { int spl; unsigned int CharNStat; int ToRecv, wRecv, ch, ttynocopy; ToRecv = sGetRxCnt(cp); if(ToRecv == 0) return; /* If status indicates there are errored characters in the FIFO, then enter status mode (a word in FIFO holds characters and status) */ if(ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { if(!(ChanStatus & STATMODE)) { ChanStatus |= STATMODE; sEnRxStatusMode(cp); } } /* if we previously entered status mode then read down the FIFO one word at a time, pulling apart the character and the status. Update error counters depending on status. */ if(ChanStatus & STATMODE) { while(ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } CharNStat = rp_readch2(cp,sGetTxRxDataIO(cp)); ch = CharNStat & 0xff; if((CharNStat & STMBREAK) || (CharNStat & STMFRAMEH)) ch |= TTY_FE; else if (CharNStat & STMPARITYH) ch |= TTY_PE; else if (CharNStat & STMRCVROVRH) rp->rp_overflows++; ttyld_rint(tp, ch); ToRecv--; } /* After emtying FIFO in status mode, turn off status mode */ if(sGetRxCnt(cp) == 0) { sDisRxStatusMode(cp); } } else { /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ ToRecv = sGetRxCnt(cp); if ( tp->t_state & TS_CAN_BYPASS_L_RINT ) { if ( ToRecv > RXFIFO_SIZE ) { ToRecv = RXFIFO_SIZE; } wRecv = ToRecv >> 1; if ( wRecv ) { rp_readmultich2(cp,sGetTxRxDataIO(cp),(u_int16_t *)rp->RxBuf,wRecv); } if ( ToRecv & 1 ) { ((unsigned char *)rp->RxBuf)[(ToRecv-1)] = (u_char) rp_readch1(cp,sGetTxRxDataIO(cp)); } tk_nin += ToRecv; tk_rawcc += ToRecv; tp->t_rawcc += ToRecv; ttynocopy = b_to_q((char *)rp->RxBuf, ToRecv, &tp->t_rawq); ttwakeup(tp); } else { while (ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } ch = (u_char) rp_readch1(cp,sGetTxRxDataIO(cp)); spl = spltty(); ttyld_rint(tp, ch); splx(spl); ToRecv--; } } } } static void rp_handle_port(struct rp_port *rp) { CHANNEL_t *cp; struct tty *tp; unsigned int IntMask, ChanStatus; if(!rp) return; cp = &rp->rp_channel; tp = rp->rp_tty; IntMask = sGetChanIntID(cp); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(cp); if(IntMask & RXF_TRIG) if(!(tp->t_state & TS_TBLOCK) && (tp->t_state & TS_CARR_ON) && (tp->t_state & TS_ISOPEN)) { rp_do_receive(rp, tp, cp, ChanStatus); } if(IntMask & DELTA_CD) { if(ChanStatus & CD_ACT) { if(!(tp->t_state & TS_CARR_ON) ) { (void)ttyld_modem(tp, 1); } } else { if((tp->t_state & TS_CARR_ON)) { (void)ttyld_modem(tp, 0); if(ttyld_modem(tp, 0) == 0) { rphardclose(rp); } } } } /* oldcts = rp->rp_cts; rp->rp_cts = ((ChanStatus & CTS_ACT) != 0); if(oldcts != rp->rp_cts) { printf("CTS change (now %s)... on port %d\n", rp->rp_cts ? "on" : "off", rp->rp_port); } */ } static void rp_do_poll(void *not_used) { CONTROLLER_t *ctl; struct rp_port *rp; struct tty *tp; int unit, aiop, ch, line, count; unsigned char CtlMask, AiopMask; for(unit = 0; unit < rp_ndevs; unit++) { rp = rp_addr(unit); ctl = rp->rp_ctlp; CtlMask = ctl->ctlmask(ctl); for(aiop=0; CtlMask; CtlMask >>=1, aiop++) { if(CtlMask & 1) { AiopMask = sGetAiopIntStatus(ctl, aiop); for(ch = 0; AiopMask; AiopMask >>=1, ch++) { if(AiopMask & 1) { line = (unit << 5) | (aiop << 3) | ch; rp = rp_table(line); rp_handle_port(rp); } } } } for(line = 0, rp = rp_addr(unit); line < rp_num_ports[unit]; line++, rp++) { tp = rp->rp_tty; if((tp->t_state & TS_BUSY) && (tp->t_state & TS_ISOPEN)) { count = sGetTxCnt(&rp->rp_channel); if(count == 0) tp->t_state &= ~(TS_BUSY); if(!(tp->t_state & TS_TTSTOP) && (count <= rp->rp_restart)) { ttyld_start(tp); } } } } if(rp_num_ports_open) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } int rp_attachcommon(CONTROLLER_T *ctlp, int num_aiops, int num_ports) { int oldspl, unit; int num_chan; int aiop, chan, port; int ChanStatus, line, i, count; int retval; struct rp_port *rp; struct tty *tty; dev_t *dev_nodes; unit = device_get_unit(ctlp->dev); printf("RocketPort%d (Version %s) %d ports.\n", unit, RocketPortVersion, num_ports); rp_num_ports[unit] = num_ports; ctlp->rp = rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if (rp == NULL) { device_printf(ctlp->dev, "rp_attachcommon: Could not malloc rp_ports structures.\n"); retval = ENOMEM; goto nogo; } count = unit * 32; /* board times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); ctlp->tty = tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT | M_ZERO); if(tty == NULL) { device_printf(ctlp->dev, "rp_attachcommon: Could not malloc tty structures.\n"); retval = ENOMEM; goto nogo; } oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); dev_nodes = ctlp->dev_nodes = malloc(sizeof(*(ctlp->dev_nodes)) * rp_num_ports[unit] * 6, M_DEVBUF, M_NOWAIT | M_ZERO); if(ctlp->dev_nodes == NULL) { device_printf(ctlp->dev, "rp_attachcommon: Could not malloc device node structures.\n"); retval = ENOMEM; goto nogo; } for (i = 0 ; i < rp_num_ports[unit] ; i++) { *(dev_nodes++) = make_dev(&rp_cdevsw, ((unit + 1) << 16) | i, UID_ROOT, GID_WHEEL, 0666, "ttyR%c", i <= 9 ? '0' + i : 'a' + i - 10); *(dev_nodes++) = make_dev(&rp_cdevsw, ((unit + 1) << 16) | i | 0x20, UID_ROOT, GID_WHEEL, 0666, "ttyiR%c", i <= 9 ? '0' + i : 'a' + i - 10); *(dev_nodes++) = make_dev(&rp_cdevsw, ((unit + 1) << 16) | i | 0x40, UID_ROOT, GID_WHEEL, 0666, "ttylR%c", i <= 9 ? '0' + i : 'a' + i - 10); *(dev_nodes++) = make_dev(&rp_cdevsw, ((unit + 1) << 16) | i | 0x80, UID_ROOT, GID_WHEEL, 0666, "cuaR%c", i <= 9 ? '0' + i : 'a' + i - 10); *(dev_nodes++) = make_dev(&rp_cdevsw, ((unit + 1) << 16) | i | 0xa0, UID_ROOT, GID_WHEEL, 0666, "cuaiR%c", i <= 9 ? '0' + i : 'a' + i - 10); *(dev_nodes++) = make_dev(&rp_cdevsw, ((unit + 1) << 16) | i | 0xc0, UID_ROOT, GID_WHEEL, 0666, "cualR%c", i <= 9 ? '0' + i : 'a' + i - 10); } port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; #if notdef ChanStatus = sGetChanStatus(&rp->rp_channel); #endif /* notdef */ if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { device_printf(ctlp->dev, "RocketPort sInitChan(%d, %d, %d) failed.\n", unit, aiop, chan); retval = ENXIO; goto nogo; } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; } } rp_ndevs++; return (0); nogo: rp_releaseresource(ctlp); return (retval); } void rp_releaseresource(CONTROLLER_t *ctlp) { int i, s, unit; unit = device_get_unit(ctlp->dev); if (ctlp->rp != NULL) { s = spltty(); for (i = 0 ; i < sizeof(p_rp_addr) / sizeof(*p_rp_addr) ; i++) if (p_rp_addr[i] == ctlp->rp) p_rp_addr[i] = NULL; for (i = 0 ; i < sizeof(p_rp_table) / sizeof(*p_rp_table) ; i++) if (p_rp_table[i] == ctlp->rp) p_rp_table[i] = NULL; splx(s); free(ctlp->rp, M_DEVBUF); ctlp->rp = NULL; } if (ctlp->tty != NULL) { free(ctlp->tty, M_DEVBUF); ctlp->tty = NULL; } if (ctlp->dev != NULL) { for (i = 0 ; i < rp_num_ports[unit] * 6 ; i++) destroy_dev(ctlp->dev_nodes[i]); free(ctlp->dev_nodes, M_DEVBUF); ctlp->dev = NULL; } } static int rpopen(dev, flag, mode, td) dev_t dev; int flag, mode; struct thread *td; { struct rp_port *rp; int unit, port, mynor, umynor, flags; /* SG */ struct tty *tp; int oldspl, error; unsigned int IntMask, ChanStatus; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; if (rp_addr(unit) == NULL) return (ENXIO); if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; /* rp->rp_tty = &rp_tty[rp->rp_port]; */ tp = rp->rp_tty; dev->si_tty = tp; oldspl = spltty(); open_top: while(rp->state & ~SET_DTR) { error = tsleep(&rp->dtr_wait, TTIPRI | PCATCH, "rpdtr", 0); if(error != 0) goto out; } if(tp->t_state & TS_ISOPEN) { if(IS_CALLOUT(dev)) { if(!rp->active_out) { error = EBUSY; goto out; } } else { if(rp->active_out) { if(flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&rp->active_out, TTIPRI | PCATCH, "rpbi", 0); if(error != 0) goto out; goto open_top; } } if(tp->t_state & TS_XCLUDE && suser(td) != 0) { splx(oldspl); error = EBUSY; goto out2; } } else { tp->t_dev = dev; tp->t_param = rpparam; tp->t_oproc = rpstart; tp->t_stop = rpstop; tp->t_line = 0; tp->t_termios = IS_CALLOUT(dev) ? rp->it_out : rp->it_in; tp->t_ififosize = 512; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; flags = 0; flags |= SET_RTS; flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); rp_writech4(&rp->rp_channel,_INDX_ADDR, *(DWord_t *) &(rp->rp_channel.TxControl[0])); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sFlushRxFIFO(&rp->rp_channel); sFlushTxFIFO(&rp->rp_channel); sEnInterrupts(&rp->rp_channel, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN)); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sClrTxXOFF(&rp->rp_channel); /* sDisRTSFlowCtl(&rp->rp_channel); sDisCTSFlowCtl(&rp->rp_channel); */ sDisTxSoftFlowCtl(&rp->rp_channel); sStartRxProcessor(&rp->rp_channel); sEnRxFIFO(&rp->rp_channel); sEnTransmit(&rp->rp_channel); /* sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); */ ++rp->wopeners; error = rpparam(tp, &tp->t_termios); --rp->wopeners; if(error != 0) { splx(oldspl); return(error); } rp_num_ports_open++; IntMask = sGetChanIntID(&rp->rp_channel); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(&rp->rp_channel); if((IntMask & DELTA_CD) || IS_CALLOUT(dev)) { if((ChanStatus & CD_ACT) || IS_CALLOUT(dev)) { (void)ttyld_modem(tp, 1); } } if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } if(!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) && !(tp->t_state & TS_CARR_ON) && !(IS_CALLOUT(dev))) { ++rp->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rpdcd", 0); --rp->wopeners; if(error != 0) goto out; goto open_top; } error = ttyld_open(tp, dev); ttyldoptim(tp); if(tp->t_state & TS_ISOPEN && IS_CALLOUT(dev)) rp->active_out = TRUE; /* if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); */ out: splx(oldspl); if(!(tp->t_state & TS_ISOPEN) && rp->wopeners == 0) { rphardclose(rp); } out2: if (error == 0) device_busy(rp->rp_ctlp->dev); return(error); } static int rpclose(dev, flag, mode, td) dev_t dev; int flag, mode; struct thread *td; { int oldspl, unit, mynor, umynor, port; /* SG */ struct rp_port *rp; struct tty *tp; CHANNEL_t *cp; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; cp = &rp->rp_channel; tp = rp->rp_tty; oldspl = spltty(); ttyld_close(tp, flag); ttyldoptim(tp); rphardclose(rp); tp->t_state &= ~TS_BUSY; ttyclose(tp); splx(oldspl); device_unbusy(rp->rp_ctlp->dev); return(0); } static void rphardclose(struct rp_port *rp) { int mynor; struct tty *tp; CHANNEL_t *cp; cp = &rp->rp_channel; tp = rp->rp_tty; mynor = MINOR_MAGIC(tp->t_dev); sFlushRxFIFO(cp); sFlushTxFIFO(cp); sDisTransmit(cp); sDisInterrupts(cp, TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN); sDisRTSFlowCtl(cp); sDisCTSFlowCtl(cp); sDisTxSoftFlowCtl(cp); sClrTxXOFF(cp); if(tp->t_cflag&HUPCL || !(tp->t_state&TS_ISOPEN) || !rp->active_out) { sClrDTR(cp); } if(IS_CALLOUT(tp->t_dev)) { sClrDTR(cp); } if(rp->dtr_wait != 0) { timeout(rpdtrwakeup, rp, rp->dtr_wait); rp->state |= ~SET_DTR; } rp->active_out = FALSE; wakeup(&rp->active_out); wakeup(TSA_CARR_ON(tp)); } static int rpwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, mynor, port, umynor, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; while(rp->rp_disable_writes) { rp->rp_waiting = 1; error = ttysleep(tp, (caddr_t)rp, TTOPRI|PCATCH, "rp_write", 0); if (error) return(error); } error = ttyld_write(tp, uio, flag); return error; } static void rpdtrwakeup(void *chan) { struct rp_port *rp; rp = (struct rp_port *)chan; rp->state &= SET_DTR; wakeup(&rp->dtr_wait); } static int rpioctl(dev, cmd, data, flag, td) dev_t dev; u_long cmd; caddr_t data; int flag; struct thread *td; { struct rp_port *rp; CHANNEL_t *cp; struct tty *tp; int unit, mynor, port, umynor; /* SG */ int oldspl; int error = 0; int arg, flags, result, ChanStatus; struct termios *t; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; if(IS_CONTROL(dev)) { struct termios *ct; switch (IS_CONTROL(dev)) { case CONTROL_INIT_STATE: ct = IS_CALLOUT(dev) ? &rp->it_out : &rp->it_in; break; case CONTROL_LOCK_STATE: ct = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; break; default: return(ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(td); if(error != 0) return(error); *ct = *(struct termios *)data; return(0); case TIOCGETA: *(struct termios *)data = *ct; return(0); case TIOCGETD: *(int *)data = TTYDISC; return(0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return(0); default: return(ENOTTY); } } tp = rp->rp_tty; cp = &rp->rp_channel; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if(error != 0) return(error); if(cmd != oldcmd) { data = (caddr_t)&term; } #endif if((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for(cc = 0; cc < NCCS; ++cc) if(lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if(lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if(lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } t = &tp->t_termios; error = ttyioctl(dev, cmd, data, flag, td); ttyldoptim(tp); if(error != ENOTTY) return(error); oldspl = spltty(); flags = rp->rp_channel.TxControl[3]; switch(cmd) { case TIOCSBRK: sSendBreak(&rp->rp_channel); break; case TIOCCBRK: sClrBreak(&rp->rp_channel); break; case TIOCSDTR: sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); break; case TIOCCDTR: sClrDTR(&rp->rp_channel); break; case TIOCMSET: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); rp_writech4(&rp->rp_channel,_INDX_ADDR, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIS: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] |= flags; rp_writech4(&rp->rp_channel,_INDX_ADDR, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIC: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] &= ~flags; rp_writech4(&rp->rp_channel,_INDX_ADDR, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMGET: ChanStatus = sGetChanStatusLo(&rp->rp_channel); flags = rp->rp_channel.TxControl[3]; result = TIOCM_LE; /* always on while open for some reason */ result |= (((flags & SET_DTR) ? TIOCM_DTR : 0) | ((flags & SET_RTS) ? TIOCM_RTS : 0) | ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0)); if(rp->rp_channel.RxControl[2] & RTSFC_EN) { result |= TIOCM_RTS; } *(int *)data = result; break; case TIOCMSDTRWAIT: error = suser(td); if(error != 0) { splx(oldspl); return(error); } rp->dtr_wait = *(int *)data * hz/100; break; case TIOCMGDTRWAIT: *(int *)data = rp->dtr_wait * 100/hz; break; default: splx(oldspl); return ENOTTY; } splx(oldspl); return(0); } static struct speedtab baud_table[] = { {B0, 0}, {B50, BRD50}, {B75, BRD75}, {B110, BRD110}, {B134, BRD134}, {B150, BRD150}, {B200, BRD200}, {B300, BRD300}, {B600, BRD600}, {B1200, BRD1200}, {B1800, BRD1800}, {B2400, BRD2400}, {B4800, BRD4800}, {B9600, BRD9600}, {B19200, BRD19200}, {B38400, BRD38400}, {B7200, BRD7200}, {B14400, BRD14400}, {B57600, BRD57600}, {B76800, BRD76800}, {B115200, BRD115200}, {B230400, BRD230400}, {-1, -1} }; static int rpparam(tp, t) struct tty *tp; struct termios *t; { struct rp_port *rp; CHANNEL_t *cp; int unit, mynor, port, umynor; /* SG */ int oldspl, cflag, iflag, oflag, lflag; int ospeed; #ifdef RPCLOCAL int devshift; #endif umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; oldspl = spltty(); cflag = t->c_cflag; #ifdef RPCLOCAL devshift = umynor / 32; devshift = 1 << devshift; if ( devshift & RPCLOCAL ) { cflag |= CLOCAL; } #endif iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; ospeed = ttspeedtab(t->c_ispeed, baud_table); if(ospeed < 0 || t->c_ispeed != t->c_ospeed) return(EINVAL); tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = cflag; tp->t_iflag = iflag; tp->t_oflag = oflag; tp->t_lflag = lflag; if(t->c_ospeed == 0) { sClrDTR(cp); return(0); } rp->rp_fifo_lw = ((t->c_ospeed*2) / 1000) +1; /* Set baud rate ----- we only pay attention to ispeed */ sSetDTR(cp); sSetRTS(cp); sSetBaud(cp, ospeed); if(cflag & CSTOPB) { sSetStop2(cp); } else { sSetStop1(cp); } if(cflag & PARENB) { sEnParity(cp); if(cflag & PARODD) { sSetOddParity(cp); } else { sSetEvenParity(cp); } } else { sDisParity(cp); } if((cflag & CSIZE) == CS8) { sSetData8(cp); rp->rp_imask = 0xFF; } else { sSetData7(cp); rp->rp_imask = 0x7F; } if(iflag & ISTRIP) { rp->rp_imask &= 0x7F; } if(cflag & CLOCAL) { rp->rp_intmask &= ~DELTA_CD; } else { rp->rp_intmask |= DELTA_CD; } /* Put flow control stuff here */ if(cflag & CCTS_OFLOW) { sEnCTSFlowCtl(cp); } else { sDisCTSFlowCtl(cp); } if(cflag & CRTS_IFLOW) { rp->rp_rts_iflow = 1; } else { rp->rp_rts_iflow = 0; } if(cflag & CRTS_IFLOW) { sEnRTSFlowCtl(cp); } else { sDisRTSFlowCtl(cp); } ttyldoptim(tp); if((cflag & CLOCAL) || (sGetChanStatusLo(cp) & CD_ACT)) { tp->t_state |= TS_CARR_ON; wakeup(TSA_CARR_ON(tp)); } /* tp->t_state |= TS_CAN_BYPASS_L_RINT; flags = rp->rp_channel.TxControl[3]; if(flags & SET_DTR) else if(flags & SET_RTS) else */ splx(oldspl); return(0); } static void rpstart(tp) struct tty *tp; { struct rp_port *rp; CHANNEL_t *cp; struct clist *qp; int unit, mynor, port, umynor; /* SG */ char flags; int spl, xmit_fifo_room; int count, wcount; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; flags = rp->rp_channel.TxControl[3]; spl = spltty(); if(tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(spl); return; } if(rp->rp_xmit_stopped) { sEnTransmit(cp); rp->rp_xmit_stopped = 0; } count = sGetTxCnt(cp); if(tp->t_outq.c_cc == 0) { if((tp->t_state & TS_BUSY) && (count == 0)) { tp->t_state &= ~TS_BUSY; } ttwwakeup(tp); splx(spl); return; } xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); qp = &tp->t_outq; if(xmit_fifo_room > 0 && qp->c_cc > 0) { tp->t_state |= TS_BUSY; count = q_to_b( qp, (char *)rp->TxBuf, xmit_fifo_room ); wcount = count >> 1; if ( wcount ) { rp_writemultich2(cp, sGetTxRxDataIO(cp), (u_int16_t *)rp->TxBuf, wcount); } if ( count & 1 ) { rp_writech1(cp, sGetTxRxDataIO(cp), ((unsigned char *)(rp->TxBuf))[(count-1)]); } } rp->rp_restart = (qp->c_cc > 0) ? rp->rp_fifo_lw : 0; ttwwakeup(tp); splx(spl); } static void rpstop(tp, flag) register struct tty *tp; int flag; { struct rp_port *rp; CHANNEL_t *cp; int unit, mynor, port, umynor; /* SG */ int spl; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; spl = spltty(); if(tp->t_state & TS_BUSY) { if((tp->t_state&TS_TTSTOP) == 0) { sFlushTxFIFO(cp); } else { if(rp->rp_xmit_stopped == 0) { sDisTransmit(cp); rp->rp_xmit_stopped = 1; } } } splx(spl); rpstart(tp); } Index: head/sys/dev/si/si.c =================================================================== --- head/sys/dev/si/si.c (revision 130343) +++ head/sys/dev/si/si.c (revision 130344) @@ -1,2145 +1,2145 @@ /* * Device driver for Specialix range (SI/XIO) of serial line multiplexors. * * Copyright (C) 1990, 1992, 1998 Specialix International, * Copyright (C) 1993, Andy Rutter * Copyright (C) 2000, Peter Wemm * * Originally derived from: SunOS 4.x version * Ported from BSDI version to FreeBSD by Peter Wemm. * * 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 * notices, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notices, 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 Andy Rutter of * Advanced Methods and Tools Ltd. based on original information * from Specialix International. * 4. Neither the name of Advanced Methods and Tools, nor Specialix * International may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``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 AUTHORS BE LIABLE. * */ #include __FBSDID("$FreeBSD$"); #ifndef lint static const char si_copyright1[] = "@(#) Copyright (C) Specialix International, 1990,1992,1998", si_copyright2[] = "@(#) Copyright (C) Andy Rutter 1993", si_copyright3[] = "@(#) Copyright (C) Peter Wemm 2000"; #endif /* not lint */ #include "opt_compat.h" #include "opt_debug_si.h" #include "opt_tty.h" #include #include -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This device driver is designed to interface the Specialix International * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA, * EISA or PCI bus machine. * * The controller is interfaced to the host via dual port RAM * and an interrupt. * * The code for the Host 1 (very old ISA cards) has not been tested. */ #define POLL /* turn on poller to scan for lost interrupts */ #define REALPOLL /* on each poll, scan for work regardless */ #define POLLHZ (hz/10) /* 10 times per second */ #define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE) #define INT_COUNT 25000 /* max of 125 ints per second */ #define JET_INT_COUNT 100 /* max of 100 ints per second */ #define RXINT_COUNT 1 /* one rxint per 10 milliseconds */ enum si_mctl { GET, SET, BIS, BIC }; static void si_command(struct si_port *, int, int); static int si_modem(struct si_port *, enum si_mctl, int); static void si_write_enable(struct si_port *, int); static int si_Sioctl(dev_t, u_long, caddr_t, int, struct thread *); static void si_start(struct tty *); static void si_stop(struct tty *, int); static timeout_t si_lstart; static void sihardclose(struct si_port *pp); static void sidtrwakeup(void *chan); #ifdef SI_DEBUG static char *si_mctl2str(enum si_mctl cmd); #endif static int siparam(struct tty *, struct termios *); static void si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip); static char * si_modulename(int host_type, int uart_type); static d_open_t siopen; static d_close_t siclose; static d_write_t siwrite; static d_ioctl_t siioctl; static struct cdevsw si_cdevsw = { .d_version = D_VERSION, .d_open = siopen, .d_close = siclose, .d_write = siwrite, .d_ioctl = siioctl, .d_name = "si", .d_flags = D_TTY | D_NEEDGIANT, }; static int si_Nports; static int si_Nmodules; static int si_debug = 0; /* data, not bss, so it's patchable */ SYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, ""); TUNABLE_INT("machdep.si_debug", &si_debug); static int si_numunits; devclass_t si_devclass; #ifndef B2000 /* not standard, but the hardware knows it. */ # define B2000 2000 #endif static struct speedtab bdrates[] = { { B75, CLK75, }, /* 0x0 */ { B110, CLK110, }, /* 0x1 */ { B150, CLK150, }, /* 0x3 */ { B300, CLK300, }, /* 0x4 */ { B600, CLK600, }, /* 0x5 */ { B1200, CLK1200, }, /* 0x6 */ { B2000, CLK2000, }, /* 0x7 */ { B2400, CLK2400, }, /* 0x8 */ { B4800, CLK4800, }, /* 0x9 */ { B9600, CLK9600, }, /* 0xb */ { B19200, CLK19200, }, /* 0xc */ { B38400, CLK38400, }, /* 0x2 (out of order!) */ { B57600, CLK57600, }, /* 0xd */ { B115200, CLK110, }, /* 0x1 (dupe!, 110 baud on "si") */ { -1, -1 }, }; /* populated with approx character/sec rates - translated at card * initialisation time to chars per tick of the clock */ static int done_chartimes = 0; static struct speedtab chartimes[] = { { B75, 8, }, { B110, 11, }, { B150, 15, }, { B300, 30, }, { B600, 60, }, { B1200, 120, }, { B2000, 200, }, { B2400, 240, }, { B4800, 480, }, { B9600, 960, }, { B19200, 1920, }, { B38400, 3840, }, { B57600, 5760, }, { B115200, 11520, }, { -1, -1 }, }; static volatile int in_intr = 0; /* Inside interrupt handler? */ #ifdef POLL static int si_pollrate; /* in addition to irq */ static int si_realpoll = 0; /* poll HW on timer */ SYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); static int init_finished = 0; static void si_poll(void *); #endif /* * Array of adapter types and the corresponding RAM size. The order of * entries here MUST match the ordinal of the adapter type. */ static char *si_type[] = { "EMPTY", "SIHOST", "SIMCA", /* FreeBSD does not support Microchannel */ "SIHOST2", "SIEISA", "SIPCI", "SXPCI", "SXISA", }; /* * We have to make an 8 bit version of bcopy, since some cards can't * deal with 32 bit I/O */ static void __inline si_bcopy(const void *src, void *dst, size_t len) { while (len--) *(((u_char *)dst)++) = *(((const u_char *)src)++); } static void __inline si_vbcopy(const volatile void *src, void *dst, size_t len) { while (len--) *(((u_char *)dst)++) = *(((const volatile u_char *)src)++); } static void __inline si_bcopyv(const void *src, volatile void *dst, size_t len) { while (len--) *(((volatile u_char *)dst)++) = *(((const u_char *)src)++); } /* * Attach the device. Initialize the card. */ int siattach(device_t dev) { int unit; struct si_softc *sc; struct si_port *pp; volatile struct si_channel *ccbp; volatile struct si_reg *regp; volatile caddr_t maddr; struct si_module *modp; struct speedtab *spt; int nmodule, nport, x, y; int uart_type; sc = device_get_softc(dev); unit = device_get_unit(dev); sc->sc_typename = si_type[sc->sc_type]; if (si_numunits < unit + 1) si_numunits = unit + 1; DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", unit)); #ifdef POLL if (si_pollrate == 0) { si_pollrate = POLLHZ; /* in addition to irq */ #ifdef REALPOLL si_realpoll = 1; /* scan always */ #endif } #endif DPRINT((0, DBG_AUTOBOOT, "si%d: type: %s paddr: %x maddr: %x\n", unit, sc->sc_typename, sc->sc_paddr, sc->sc_maddr)); sc->sc_ports = NULL; /* mark as uninitialised */ maddr = sc->sc_maddr; /* Stop the CPU first so it won't stomp around while we load */ switch (sc->sc_type) { case SIEISA: outb(sc->sc_iobase + 2, sc->sc_irq << 4); break; case SIPCI: *(maddr+SIPCIRESET) = 0; break; case SIJETPCI: /* fall through to JET ISA */ case SIJETISA: *(maddr+SIJETCONFIG) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0; break; case SIHOST: *(maddr+SIRESET) = 0; break; default: /* this should never happen */ printf("si%d: unsupported configuration\n", unit); return EINVAL; break; } /* OK, now lets download the download code */ if (SI_ISJET(sc->sc_type)) { DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n", unit, si3_t225_dsize)); si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr, si3_t225_dsize); DPRINT((0, DBG_DOWNLOAD, "si%d: jet_bootstrap: nbytes %d -> %x\n", unit, si3_t225_bsize, si3_t225_bootloadaddr)); si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr, si3_t225_bsize); } else { DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", unit, si2_z280_dsize)); si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr, si2_z280_dsize); } /* Now start the CPU */ switch (sc->sc_type) { case SIEISA: /* modify the download code to tell it that it's on an EISA */ *(maddr + 0x42) = 1; outb(sc->sc_iobase + 2, (sc->sc_irq << 4) | 4); (void)inb(sc->sc_iobase + 3); /* reset interrupt */ break; case SIPCI: /* modify the download code to tell it that it's on a PCI */ *(maddr+0x42) = 1; *(maddr+SIPCIRESET) = 1; *(maddr+SIPCIINTCL) = 0; break; case SIJETPCI: *(maddr+SIJETRESET) = 0; *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN; break; case SIJETISA: *(maddr+SIJETRESET) = 0; switch (sc->sc_irq) { case 9: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90; break; case 10: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0; break; case 11: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0; break; case 12: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0; break; case 15: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0; break; } break; case SIHOST: *(maddr+SIRESET_CL) = 0; *(maddr+SIINTCL_CL) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0x10; switch (sc->sc_irq) { case 11: *(maddr+SIPLIRQ11) = 0x10; break; case 12: *(maddr+SIPLIRQ12) = 0x10; break; case 15: *(maddr+SIPLIRQ15) = 0x10; break; } *(maddr+SIPLIRQCLR) = 0x10; break; default: /* this should _REALLY_ never happen */ printf("si%d: Uh, it was supported a second ago...\n", unit); return EINVAL; } DELAY(1000000); /* wait around for a second */ regp = (struct si_reg *)maddr; y = 0; /* wait max of 5 sec for init OK */ while (regp->initstat == 0 && y++ < 10) { DELAY(500000); } switch (regp->initstat) { case 0: printf("si%d: startup timeout - aborting\n", unit); sc->sc_type = SIEMPTY; return EINVAL; case 1: if (SI_ISJET(sc->sc_type)) { /* set throttle to 100 times per second */ regp->int_count = JET_INT_COUNT; /* rx_intr_count is a NOP in Jet */ } else { /* set throttle to 125 times per second */ regp->int_count = INT_COUNT; /* rx intr max of 25 times per second */ regp->rx_int_count = RXINT_COUNT; } regp->int_pending = 0; /* no intr pending */ regp->int_scounter = 0; /* reset counter */ break; case 0xff: /* * No modules found, so give up on this one. */ printf("si%d: %s - no ports found\n", unit, si_type[sc->sc_type]); return 0; default: printf("si%d: download code version error - initstat %x\n", unit, regp->initstat); return EINVAL; } /* * First time around the ports just count them in order * to allocate some memory. */ nport = 0; modp = (struct si_module *)(maddr + 0x80); for (;;) { DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); switch (modp->sm_type) { case TA4: DPRINT((0, DBG_DOWNLOAD, "si%d: Found old TA4 module, 4 ports\n", unit)); x = 4; break; case TA8: DPRINT((0, DBG_DOWNLOAD, "si%d: Found old TA8 module, 8 ports\n", unit)); x = 8; break; case TA4_ASIC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found ASIC TA4 module, 4 ports\n", unit)); x = 4; break; case TA8_ASIC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found ASIC TA8 module, 8 ports\n", unit)); x = 8; break; case MTA: DPRINT((0, DBG_DOWNLOAD, "si%d: Found CD1400 module, 8 ports\n", unit)); x = 8; break; case SXDC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found SXDC module, 8 ports\n", unit)); x = 8; break; default: printf("si%d: unknown module type %d\n", unit, modp->sm_type); goto try_next; } /* this was limited in firmware and is also a driver issue */ if ((nport + x) > SI_MAXPORTPERCARD) { printf("si%d: extra ports ignored\n", unit); goto try_next; } nport += x; si_Nports += x; si_Nmodules++; try_next: if (modp->sm_next == 0) break; modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff)); } sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport, M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->sc_ports == 0) { printf("si%d: fail to malloc memory for port structs\n", unit); return EINVAL; } sc->sc_nport = nport; /* * Scan round the ports again, this time initialising. */ pp = sc->sc_ports; nmodule = 0; modp = (struct si_module *)(maddr + 0x80); uart_type = 1000; /* arbitary, > uchar_max */ for (;;) { switch (modp->sm_type) { case TA4: nport = 4; break; case TA8: nport = 8; break; case TA4_ASIC: nport = 4; break; case TA8_ASIC: nport = 8; break; case MTA: nport = 8; break; case SXDC: nport = 8; break; default: goto try_next2; } nmodule++; ccbp = (struct si_channel *)((char *)modp + 0x100); if (uart_type == 1000) uart_type = ccbp->type; else if (uart_type != ccbp->type) printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n", unit, nmodule, ccbp->type, si_modulename(sc->sc_type, ccbp->type), uart_type, si_modulename(sc->sc_type, uart_type)); for (x = 0; x < nport; x++, pp++, ccbp++) { pp->sp_ccb = ccbp; /* save the address */ pp->sp_tty = ttymalloc(NULL); pp->sp_pend = IDLE_CLOSE; pp->sp_state = 0; /* internal flag */ pp->sp_dtr_wait = 3 * hz; pp->sp_iin.c_iflag = TTYDEF_IFLAG; pp->sp_iin.c_oflag = TTYDEF_OFLAG; pp->sp_iin.c_cflag = TTYDEF_CFLAG; pp->sp_iin.c_lflag = TTYDEF_LFLAG; termioschars(&pp->sp_iin); pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed = TTYDEF_SPEED;; pp->sp_iout = pp->sp_iin; } try_next2: if (modp->sm_next == 0) { printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n", unit, sc->sc_typename, sc->sc_nport, nmodule, uart_type, si_modulename(sc->sc_type, uart_type)); break; } modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff)); } if (done_chartimes == 0) { for (spt = chartimes ; spt->sp_speed != -1; spt++) { if ((spt->sp_code /= hz) == 0) spt->sp_code = 1; } done_chartimes = 1; } /* path name devsw minor type uid gid perm*/ for (x = 0; x < sc->sc_nport; x++) { /* sync with the manuals that start at 1 */ y = x + 1 + unit * (1 << SI_CARDSHIFT); make_dev(&si_cdevsw, x, 0, 0, 0600, "ttyA%02d", y); make_dev(&si_cdevsw, x + 0x00080, 0, 0, 0600, "cuaA%02d", y); make_dev(&si_cdevsw, x + 0x10000, 0, 0, 0600, "ttyiA%02d", y); make_dev(&si_cdevsw, x + 0x10080, 0, 0, 0600, "cuaiA%02d", y); make_dev(&si_cdevsw, x + 0x20000, 0, 0, 0600, "ttylA%02d", y); make_dev(&si_cdevsw, x + 0x20080, 0, 0, 0600, "cualA%02d", y); } make_dev(&si_cdevsw, 0x40000, 0, 0, 0600, "si_control"); return (0); } static int siopen(dev_t dev, int flag, int mode, struct thread *td) { int oldspl, error; int card, port; struct si_softc *sc; struct tty *tp; volatile struct si_channel *ccbp; struct si_port *pp; int mynor = minor(dev); /* quickly let in /dev/si_control */ if (IS_CONTROLDEV(mynor)) { if ((error = suser(td))) return(error); return(0); } card = SI_CARD(mynor); sc = devclass_get_softc(si_devclass, card); if (sc == NULL) return (ENXIO); if (sc->sc_type == SIEMPTY) { DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n", card, sc->sc_typename)); return(ENXIO); } port = SI_PORT(mynor); if (port >= sc->sc_nport) { DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n", card, sc->sc_nport)); return(ENXIO); } #ifdef POLL /* * We've now got a device, so start the poller. */ if (init_finished == 0) { timeout(si_poll, (caddr_t)0L, si_pollrate); init_finished = 1; } #endif /* initial/lock device */ if (IS_STATE(mynor)) { return(0); } pp = sc->sc_ports + port; tp = pp->sp_tty; /* the "real" tty */ dev->si_tty = tp; ccbp = pp->sp_ccb; /* Find control block */ DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%s,%x,%x,%x)\n", devtoname(dev), flag, mode, td)); oldspl = spltty(); /* Keep others out */ error = 0; open_top: while (pp->sp_state & SS_DTR_OFF) { error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialised. * handle conflicts. */ if (IS_CALLOUT(mynor)) { if (!pp->sp_active_out) { error = EBUSY; goto out; } } else { if (pp->sp_active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&pp->sp_active_out, TTIPRI|PCATCH, "sibi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && suser(td)) { DPRINT((pp, DBG_OPEN|DBG_FAIL, "already open and EXCLUSIVE set\n")); error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Avoid sleep... :-) */ DPRINT((pp, DBG_OPEN, "first open\n")); tp->t_oproc = si_start; tp->t_stop = si_stop; tp->t_param = siparam; tp->t_dev = dev; tp->t_termios = mynor & SI_CALLOUT_MASK ? pp->sp_iout : pp->sp_iin; (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); ++pp->sp_wopeners; /* in case of sleep in siparam */ error = siparam(tp, &tp->t_termios); --pp->sp_wopeners; if (error != 0) goto out; /* XXX: we should goto_top if siparam slept */ /* set initial DCD state */ pp->sp_last_hi_ip = ccbp->hi_ip; if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) { ttyld_modem(tp, 1); } } /* whoops! we beat the close! */ if (pp->sp_state & SS_CLOSING) { /* try and stop it from proceeding to bash the hardware */ pp->sp_state &= ~SS_CLOSING; } /* * Wait for DCD if necessary */ if (!(tp->t_state & TS_CARR_ON) && !IS_CALLOUT(mynor) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++pp->sp_wopeners; DPRINT((pp, DBG_OPEN, "sleeping for carrier\n")); error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0); --pp->sp_wopeners; if (error != 0) goto out; goto open_top; } error = ttyld_open(tp, dev); pp->sp_hotchar = ttyldoptim(tp); if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor)) pp->sp_active_out = TRUE; pp->sp_state |= SS_OPEN; /* made it! */ out: splx(oldspl); DPRINT((pp, DBG_OPEN, "leaving siopen\n")); if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0) sihardclose(pp); return(error); } static int siclose(dev_t dev, int flag, int mode, struct thread *td) { struct si_port *pp; struct tty *tp; int oldspl; int error = 0; int mynor = minor(dev); if (IS_SPECIAL(mynor)) return(0); oldspl = spltty(); pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%s,%x,%x,%x) sp_state:%x\n", devtoname(dev), flag, mode, td, pp->sp_state)); /* did we sleep and loose a race? */ if (pp->sp_state & SS_CLOSING) { /* error = ESOMETING? */ goto out; } /* begin race detection.. */ pp->sp_state |= SS_CLOSING; si_write_enable(pp, 0); /* block writes for ttywait() */ /* THIS MAY SLEEP IN TTYWAIT!!! */ ttyld_close(tp, flag); si_write_enable(pp, 1); /* did we sleep and somebody started another open? */ if (!(pp->sp_state & SS_CLOSING)) { /* error = ESOMETING? */ goto out; } /* ok. we are now still on the right track.. nuke the hardware */ if (pp->sp_state & SS_LSTART) { untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); pp->sp_state &= ~SS_LSTART; } sihardclose(pp); ttyclose(tp); pp->sp_state &= ~SS_OPEN; out: DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n")); splx(oldspl); return(error); } static void sihardclose(struct si_port *pp) { int oldspl; struct tty *tp; volatile struct si_channel *ccbp; oldspl = spltty(); tp = pp->sp_tty; ccbp = pp->sp_ccb; /* Find control block */ if (tp->t_cflag & HUPCL || (!pp->sp_active_out && !(ccbp->hi_ip & IP_DCD) && !(pp->sp_iin.c_cflag && CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); (void) si_command(pp, FCLOSE, SI_NOWAIT); if (pp->sp_dtr_wait != 0) { timeout(sidtrwakeup, pp, pp->sp_dtr_wait); pp->sp_state |= SS_DTR_OFF; } } pp->sp_active_out = FALSE; wakeup(&pp->sp_active_out); wakeup(TSA_CARR_ON(tp)); splx(oldspl); } /* * called at splsoftclock()... */ static void sidtrwakeup(void *chan) { struct si_port *pp; int oldspl; oldspl = spltty(); pp = (struct si_port *)chan; pp->sp_state &= ~SS_DTR_OFF; wakeup(&pp->sp_dtr_wait); splx(oldspl); } static int siwrite(dev_t dev, struct uio *uio, int flag) { struct si_port *pp; struct tty *tp; int error = 0; int mynor = minor(dev); int oldspl; if (IS_SPECIAL(mynor)) { DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n")); return(ENODEV); } pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_WRITE, "siwrite(%s,%x,%x)\n", devtoname(dev), uio, flag)); oldspl = spltty(); /* * If writes are currently blocked, wait on the "real" tty */ while (pp->sp_state & SS_BLOCKWRITE) { pp->sp_state |= SS_WAITWRITE; DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n")); if ((error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH, "siwrite", tp->t_timeout))) { if (error == EWOULDBLOCK) error = EIO; goto out; } } error = ttyld_write(tp, uio, flag); out: splx(oldspl); return (error); } static int siioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct si_port *pp; struct tty *tp; int error; int mynor = minor(dev); int oldspl; int blocked = 0; #if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif if (IS_SI_IOCTL(cmd)) return(si_Sioctl(dev, cmd, data, flag, td)); pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%s,%lx,%x,%x)\n", devtoname(dev), cmd, data, flag)); if (IS_STATE(mynor)) { struct termios *ct; switch (mynor & SI_STATE_MASK) { case SI_INIT_STATE_MASK: ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin; break; case SI_LOCK_STATE_MASK: ct = IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin; break; default: return (ENODEV); } switch (cmd) { case TIOCSETA: error = suser(td); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } /* * Do the old-style ioctl compat routines... */ #if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif /* * Do the initial / lock state business */ if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & SI_CALLOUT_MASK ? &pp->sp_lout : &pp->sp_lin; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } /* * Block user-level writes to give the ttywait() * a chance to completely drain for commands * that require the port to be in a quiescent state. */ switch (cmd) { case TIOCSETAW: case TIOCSETAF: case TIOCDRAIN: #ifdef COMPAT_43 case TIOCSETP: #endif blocked++; /* block writes for ttywait() and siparam() */ si_write_enable(pp, 0); } error = ttyioctl(dev, cmd, data, flag, td); pp->sp_hotchar = ttyldoptim(tp); if (error != ENOTTY) goto out; oldspl = spltty(); error = 0; switch (cmd) { case TIOCSBRK: si_command(pp, SBREAK, SI_WAIT); break; case TIOCCBRK: si_command(pp, EBREAK, SI_WAIT); break; case TIOCSDTR: (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); break; case TIOCCDTR: (void) si_modem(pp, SET, 0); break; case TIOCMSET: (void) si_modem(pp, SET, *(int *)data); break; case TIOCMBIS: (void) si_modem(pp, BIS, *(int *)data); break; case TIOCMBIC: (void) si_modem(pp, BIC, *(int *)data); break; case TIOCMGET: *(int *)data = si_modem(pp, GET, 0); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(td); if (error == 0) pp->sp_dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = pp->sp_dtr_wait * 100 / hz; break; default: error = ENOTTY; } splx(oldspl); out: DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error)); if (blocked) si_write_enable(pp, 1); return(error); } /* * Handle the Specialix ioctls. All MUST be called via the CONTROL device */ static int si_Sioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct si_softc *xsc; struct si_port *xpp; volatile struct si_reg *regp; struct si_tcsi *dp; struct si_pstat *sps; int *ip, error = 0; int oldspl; int card, port; int mynor = minor(dev); DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%s,%lx,%x,%x)\n", devtoname(dev), cmd, data, flag)); #if 1 DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY)); #endif if (!IS_CONTROLDEV(mynor)) { DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n")); return(ENODEV); } oldspl = spltty(); /* better safe than sorry */ ip = (int *)data; #define SUCHECK if ((error = suser(td))) goto out switch (cmd) { case TCSIPORTS: *ip = si_Nports; goto out; case TCSIMODULES: *ip = si_Nmodules; goto out; case TCSISDBG_ALL: SUCHECK; si_debug = *ip; goto out; case TCSIGDBG_ALL: *ip = si_debug; goto out; default: /* * Check that a controller for this port exists */ /* may also be a struct si_pstat, a superset of si_tcsi */ dp = (struct si_tcsi *)data; sps = (struct si_pstat *)data; card = dp->tc_card; xsc = devclass_get_softc(si_devclass, card); /* check.. */ if (xsc == NULL || xsc->sc_type == SIEMPTY) { error = ENOENT; goto out; } /* * And check that a port exists */ port = dp->tc_port; if (port < 0 || port >= xsc->sc_nport) { error = ENOENT; goto out; } xpp = xsc->sc_ports + port; regp = (struct si_reg *)xsc->sc_maddr; } switch (cmd) { case TCSIDEBUG: #ifdef SI_DEBUG SUCHECK; if (xpp->sp_debug) xpp->sp_debug = 0; else { xpp->sp_debug = DBG_ALL; DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); } break; #else error = ENODEV; goto out; #endif case TCSISDBG_LEVEL: case TCSIGDBG_LEVEL: #ifdef SI_DEBUG if (cmd == TCSIGDBG_LEVEL) { dp->tc_dbglvl = xpp->sp_debug; } else { SUCHECK; xpp->sp_debug = dp->tc_dbglvl; } break; #else error = ENODEV; goto out; #endif case TCSIGRXIT: dp->tc_int = regp->rx_int_count; break; case TCSIRXIT: SUCHECK; regp->rx_int_count = dp->tc_int; break; case TCSIGIT: dp->tc_int = regp->int_count; break; case TCSIIT: SUCHECK; regp->int_count = dp->tc_int; break; case TCSISTATE: dp->tc_int = xpp->sp_ccb->hi_ip; break; /* these next three use a different structure */ case TCSI_PORT: SUCHECK; si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); break; case TCSI_CCB: SUCHECK; si_vbcopy(xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); break; case TCSI_TTY: SUCHECK; si_bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty)); break; default: error = EINVAL; goto out; } out: splx(oldspl); return(error); /* success */ } /* * siparam() : Configure line params * called at spltty(); * this may sleep, does not flush, nor wait for drain, nor block writes * caller must arrange this if it's important.. */ static int siparam(struct tty *tp, struct termios *t) { struct si_port *pp = TP2PP(tp); volatile struct si_channel *ccbp; int oldspl, cflag, iflag, oflag, lflag; int error = 0; /* shutup gcc */ int ispeed = 0; /* shutup gcc */ int ospeed = 0; /* shutup gcc */ BYTE val; DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t)); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n", oflag, cflag, iflag, lflag)); /* XXX - if Jet host and SXDC module, use extended baud rates */ /* if not hung up.. */ if (t->c_ospeed != 0) { /* translate baud rate to firmware values */ ospeed = ttspeedtab(t->c_ospeed, bdrates); ispeed = t->c_ispeed ? ttspeedtab(t->c_ispeed, bdrates) : ospeed; /* enforce legit baud rate */ if (ospeed < 0 || ispeed < 0) return (EINVAL); } oldspl = spltty(); ccbp = pp->sp_ccb; /* ========== set hi_break ========== */ val = 0; if (iflag & IGNBRK) /* Breaks */ val |= BR_IGN; if (iflag & BRKINT) /* Interrupt on break? */ val |= BR_INT; if (iflag & PARMRK) /* Parity mark? */ val |= BR_PARMRK; if (iflag & IGNPAR) /* Ignore chars with parity errors? */ val |= BR_PARIGN; ccbp->hi_break = val; /* ========== set hi_csr ========== */ /* if not hung up.. */ if (t->c_ospeed != 0) { /* Set I/O speeds */ val = (ispeed << 4) | ospeed; } ccbp->hi_csr = val; /* ========== set hi_mr2 ========== */ val = 0; if (cflag & CSTOPB) /* Stop bits */ val |= MR2_2_STOP; else val |= MR2_1_STOP; /* * Enable H/W RTS/CTS handshaking. The default TA/MTA is * a DCE, hence the reverse sense of RTS and CTS */ /* Output Flow - RTS must be raised before data can be sent */ if (cflag & CCTS_OFLOW) val |= MR2_RTSCONT; ccbp->hi_mr2 = val; /* ========== set hi_mr1 ========== */ val = 0; if (!(cflag & PARENB)) /* Parity */ val |= MR1_NONE; else val |= MR1_WITH; if (cflag & PARODD) val |= MR1_ODD; if ((cflag & CS8) == CS8) { /* 8 data bits? */ val |= MR1_8_BITS; } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ val |= MR1_7_BITS; } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ val |= MR1_6_BITS; } else { /* Must be 5 */ val |= MR1_5_BITS; } /* * Enable H/W RTS/CTS handshaking. The default TA/MTA is * a DCE, hence the reverse sense of RTS and CTS */ /* Input Flow - CTS is raised when port is ready to receive data */ if (cflag & CRTS_IFLOW) val |= MR1_CTSCONT; ccbp->hi_mr1 = val; /* ========== set hi_mask ========== */ val = 0xff; if ((cflag & CS8) == CS8) { /* 8 data bits? */ val &= 0xFF; } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ val &= 0x7F; } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ val &= 0x3F; } else { /* Must be 5 */ val &= 0x1F; } if (iflag & ISTRIP) val &= 0x7F; ccbp->hi_mask = val; /* ========== set hi_prtcl ========== */ val = SP_DCEN; /* Monitor DCD always, or TIOCMGET misses it */ if (iflag & IXANY) val |= SP_TANY; if (iflag & IXON) val |= SP_TXEN; if (iflag & IXOFF) val |= SP_RXEN; if (iflag & INPCK) val |= SP_PAEN; ccbp->hi_prtcl = val; /* ========== set hi_{rx|tx}{on|off} ========== */ /* XXX: the card TOTALLY shields us from the flow control... */ ccbp->hi_txon = t->c_cc[VSTART]; ccbp->hi_txoff = t->c_cc[VSTOP]; ccbp->hi_rxon = t->c_cc[VSTART]; ccbp->hi_rxoff = t->c_cc[VSTOP]; /* ========== send settings to the card ========== */ /* potential sleep here */ if (ccbp->hi_stat == IDLE_CLOSE) /* Not yet open */ si_command(pp, LOPEN, SI_WAIT); /* open it */ else si_command(pp, CONFIG, SI_WAIT); /* change params */ /* ========== set DTR etc ========== */ /* Hangup if ospeed == 0 */ if (t->c_ospeed == 0) { (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); } else { /* * If the previous speed was 0, may need to re-enable * the modem signals */ (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); } DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n", ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break)); splx(oldspl); return(error); } /* * Enable or Disable the writes to this channel... * "state" -> enabled = 1; disabled = 0; */ static void si_write_enable(struct si_port *pp, int state) { int oldspl; oldspl = spltty(); if (state) { pp->sp_state &= ~SS_BLOCKWRITE; if (pp->sp_state & SS_WAITWRITE) { pp->sp_state &= ~SS_WAITWRITE; /* thunder away! */ wakeup(pp); } } else { pp->sp_state |= SS_BLOCKWRITE; } splx(oldspl); } /* * Set/Get state of modem control lines. * Due to DCE-like behaviour of the adapter, some signals need translation: * TIOCM_DTR DSR * TIOCM_RTS CTS */ static int si_modem(struct si_port *pp, enum si_mctl cmd, int bits) { volatile struct si_channel *ccbp; int x; DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits)); ccbp = pp->sp_ccb; /* Find channel address */ switch (cmd) { case GET: x = ccbp->hi_ip; bits = TIOCM_LE; if (x & IP_DCD) bits |= TIOCM_CAR; if (x & IP_DTR) bits |= TIOCM_DTR; if (x & IP_RTS) bits |= TIOCM_RTS; if (x & IP_RI) bits |= TIOCM_RI; return(bits); case SET: ccbp->hi_op &= ~(OP_DSR|OP_CTS); /* fall through */ case BIS: x = 0; if (bits & TIOCM_DTR) x |= OP_DSR; if (bits & TIOCM_RTS) x |= OP_CTS; ccbp->hi_op |= x; break; case BIC: if (bits & TIOCM_DTR) ccbp->hi_op &= ~OP_DSR; if (bits & TIOCM_RTS) ccbp->hi_op &= ~OP_CTS; } return 0; } /* * Handle change of modem state */ static void si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip) { /* if a modem dev */ if (hi_ip & IP_DCD) { if (!(pp->sp_last_hi_ip & IP_DCD)) { DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n", tp->t_line)); (void)ttyld_modem(tp, 1); } } else { if (pp->sp_last_hi_ip & IP_DCD) { DPRINT((pp, DBG_INTR, "modem carr off\n")); if (ttyld_modem(tp, 0)) (void) si_modem(pp, SET, 0); } } pp->sp_last_hi_ip = hi_ip; } /* * Poller to catch missed interrupts. * * Note that the SYSV Specialix drivers poll at 100 times per second to get * better response. We could really use a "periodic" version timeout(). :-) */ #ifdef POLL static void si_poll(void *nothing) { struct si_softc *sc; int i; volatile struct si_reg *regp; struct si_port *pp; int lost, oldspl, port; DPRINT((0, DBG_POLL, "si_poll()\n")); oldspl = spltty(); if (in_intr) goto out; lost = 0; for (i = 0; i < si_numunits; i++) { sc = devclass_get_softc(si_devclass, i); if (sc == NULL || sc->sc_type == SIEMPTY) continue; regp = (struct si_reg *)sc->sc_maddr; /* * See if there has been a pending interrupt for 2 seconds * or so. The test (int_scounter >= 200) won't correspond * to 2 seconds if int_count gets changed. */ if (regp->int_pending != 0) { if (regp->int_scounter >= 200 && regp->initstat == 1) { printf("si%d: lost intr\n", i); lost++; } } else { regp->int_scounter = 0; } /* * gripe about no input flow control.. */ pp = sc->sc_ports; for (port = 0; port < sc->sc_nport; pp++, port++) { if (pp->sp_delta_overflows > 0) { printf("si%d: %d tty level buffer overflows\n", i, pp->sp_delta_overflows); pp->sp_delta_overflows = 0; } } } if (lost || si_realpoll) si_intr(NULL); /* call intr with fake vector */ out: splx(oldspl); timeout(si_poll, (caddr_t)0L, si_pollrate); } #endif /* ifdef POLL */ /* * The interrupt handler polls ALL ports on ALL adapters each time * it is called. */ static BYTE si_rxbuf[SI_BUFFERSIZE]; /* input staging area */ static BYTE si_txbuf[SI_BUFFERSIZE]; /* output staging area */ void si_intr(void *arg) { struct si_softc *sc; struct si_port *pp; volatile struct si_channel *ccbp; struct tty *tp; volatile caddr_t maddr; BYTE op, ip; int x, card, port, n, i, isopen; volatile BYTE *z; BYTE c; sc = arg; DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n")); if (in_intr) return; in_intr = 1; /* * When we get an int we poll all the channels and do ALL pending * work, not just the first one we find. This allows all cards to * share the same vector. * * XXX - But if we're sharing the vector with something that's NOT * a SI/XIO/SX card, we may be making more work for ourselves. */ for (card = 0; card < si_numunits; card++) { sc = devclass_get_softc(si_devclass, card); if (sc == NULL || sc->sc_type == SIEMPTY) continue; /* * First, clear the interrupt */ switch(sc->sc_type) { case SIHOST: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; /* flag nothing pending */ *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ break; case SIHOST2: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPLIRQCLR) = 0x00; *(maddr+SIPLIRQCLR) = 0x10; break; case SIPCI: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPCIINTCL) = 0x0; break; case SIJETPCI: /* fall through to JETISA case */ case SIJETISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIJETINTCL) = 0x0; break; case SIEISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; (void)inb(sc->sc_iobase + 3); break; case SIEMPTY: default: continue; } ((volatile struct si_reg *)maddr)->int_scounter = 0; /* * check each port */ for (pp = sc->sc_ports, port = 0; port < sc->sc_nport; pp++, port++) { ccbp = pp->sp_ccb; tp = pp->sp_tty; /* * See if a command has completed ? */ if (ccbp->hi_stat != pp->sp_pend) { DPRINT((pp, DBG_INTR, "si_intr hi_stat = 0x%x, pend = %d\n", ccbp->hi_stat, pp->sp_pend)); switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case CONFIG: case SBREAK: case EBREAK: pp->sp_pend = ccbp->hi_stat; /* sleeping in si_command */ wakeup(&pp->sp_state); break; default: pp->sp_pend = ccbp->hi_stat; } } /* * Continue on if it's closed */ if (ccbp->hi_stat == IDLE_CLOSE) { continue; } /* * Do modem state change if not a local device */ si_modem_state(pp, tp, ccbp->hi_ip); /* * Check to see if we should 'receive' characters. */ if (tp->t_state & TS_CONNECTED && tp->t_state & TS_ISOPEN) isopen = 1; else isopen = 0; /* * Do input break processing */ if (ccbp->hi_state & ST_BREAK) { if (isopen) { ttyld_rint(tp, TTY_BI); } ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ DPRINT((pp, DBG_INTR, "si_intr break\n")); } /* * Do RX stuff - if not open then dump any characters. * XXX: This is VERY messy and needs to be cleaned up. * * XXX: can we leave data in the host adapter buffer * when the clists are full? That may be dangerous * if the user cannot get an interrupt signal through. */ more_rx: /* XXX Sorry. the nesting was driving me bats! :-( */ if (!isopen) { ccbp->hi_rxopos = ccbp->hi_rxipos; goto end_rx; } /* * If the tty input buffers are blocked, stop emptying * the incoming buffers and let the auto flow control * assert.. */ if (tp->t_state & TS_TBLOCK) { goto end_rx; } /* * Process read characters if not skipped above */ op = ccbp->hi_rxopos; ip = ccbp->hi_rxipos; c = ip - op; if (c == 0) { goto end_rx; } n = c & 0xff; if (n > 250) n = 250; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * Suck characters out of host card buffer into the * "input staging buffer" - so that we dont leave the * host card in limbo while we're possibly echoing * characters and possibly flushing input inside the * ldisc l_rint() routine. */ if (n <= SI_BUFFERSIZE - op) { DPRINT((pp, DBG_INTR, "\tsingle copy\n")); z = ccbp->hi_rxbuf + op; si_vbcopy(z, si_rxbuf, n); op += n; } else { x = SI_BUFFERSIZE - op; DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x)); z = ccbp->hi_rxbuf + op; si_vbcopy(z, si_rxbuf, x); DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n - x)); z = ccbp->hi_rxbuf; si_vbcopy(z, si_rxbuf + x, n - x); op += n; } /* clear collected characters from buffer */ ccbp->hi_rxopos = op; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * at this point... * n = number of chars placed in si_rxbuf */ /* * Avoid the grotesquely inefficient lineswitch * routine (ttyinput) in "raw" mode. It usually * takes about 450 instructions (that's without * canonical processing or echo!). slinput is * reasonably fast (usually 40 instructions * plus call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { /* block if the driver supports it */ if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER && (tp->t_cflag & CRTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += n; tk_rawcc += n; tp->t_rawcc += n; pp->sp_delta_overflows += b_to_q((char *)si_rxbuf, n, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; si_start(tp); } } else { /* * It'd be nice to not have to go through the * function call overhead for each char here. * It'd be nice to block input it, saving a * loop here and the call/return overhead. */ for(x = 0; x < n; x++) { i = si_rxbuf[x]; if (ttyld_rint(tp, i) == -1) { pp->sp_delta_overflows++; } } } goto more_rx; /* try for more until RXbuf is empty */ end_rx: /* XXX: Again, sorry about the gotos.. :-) */ /* * Do TX stuff */ ttyld_start(tp); } /* end of for (all ports on this controller) */ } /* end of for (all controllers) */ in_intr = 0; DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n")); } /* * Nudge the transmitter... * * XXX: I inherited some funny code here. It implies the host card only * interrupts when the transmit buffer reaches the low-water-mark, and does * not interrupt when it's actually hits empty. In some cases, we have * processes waiting for complete drain, and we need to simulate an interrupt * about when we think the buffer is going to be empty (and retry if not). * I really am not certain about this... I *need* the hardware manuals. */ static void si_start(struct tty *tp) { struct si_port *pp; volatile struct si_channel *ccbp; struct clist *qp; BYTE ipos; int nchar; int oldspl, count, n, amount, buffer_full; oldspl = spltty(); qp = &tp->t_outq; pp = TP2PP(tp); DPRINT((pp, DBG_ENTRY|DBG_START, "si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n", tp, tp->t_state, pp->sp_state, qp->c_cc)); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; buffer_full = 0; ccbp = pp->sp_ccb; count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; DPRINT((pp, DBG_START, "count %d\n", (BYTE)count)); while ((nchar = qp->c_cc) > 0) { if ((BYTE)count >= 255) { buffer_full++; break; } amount = min(nchar, (255 - (BYTE)count)); ipos = (unsigned int)ccbp->hi_txipos; n = q_to_b(&tp->t_outq, si_txbuf, amount); /* will it fit in one lump? */ if ((SI_BUFFERSIZE - ipos) >= n) { si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], n); } else { si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], SI_BUFFERSIZE - ipos); si_bcopyv(si_txbuf + (SI_BUFFERSIZE - ipos), &ccbp->hi_txbuf[0], n - (SI_BUFFERSIZE - ipos)); } ccbp->hi_txipos += n; count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; } if (count != 0 && nchar == 0) { tp->t_state |= TS_BUSY; } else { tp->t_state &= ~TS_BUSY; } /* wakeup time? */ ttwwakeup(tp); DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n", (BYTE)count, nchar, tp->t_state)); if (tp->t_state & TS_BUSY) { int time; time = ttspeedtab(tp->t_ospeed, chartimes); if (time > 0) { if (time < nchar) time = nchar / time; else time = 2; } else { DPRINT((pp, DBG_START, "bad char time value! %d\n", time)); time = hz/10; } if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) { untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); } else { pp->sp_state |= SS_LSTART; } DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time)); pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time); } out: splx(oldspl); DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n")); } /* * Note: called at splsoftclock from the timeout code * This has to deal with two things... cause wakeups while waiting for * tty drains on last process exit, and call l_start at about the right * time for protocols like ppp. */ static void si_lstart(void *arg) { struct si_port *pp = arg; struct tty *tp; int oldspl; DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n", pp, pp->sp_state)); oldspl = spltty(); if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) { splx(oldspl); return; } pp->sp_state &= ~SS_LSTART; pp->sp_state |= SS_INLSTART; tp = pp->sp_tty; /* deal with the process exit case */ ttwwakeup(tp); /* nudge protocols - eg: ppp */ ttyld_start(tp); pp->sp_state &= ~SS_INLSTART; splx(oldspl); } /* * Stop output on a line. called at spltty(); */ static void si_stop(struct tty *tp, int rw) { volatile struct si_channel *ccbp; struct si_port *pp; pp = TP2PP(tp); ccbp = pp->sp_ccb; DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "si_stop(%x,%x)\n", tp, rw)); /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ if (rw & FWRITE) { /* what level are we meant to be flushing anyway? */ if (tp->t_state & TS_BUSY) { si_command(TP2PP(tp), WFLUSH, SI_NOWAIT); tp->t_state &= ~TS_BUSY; ttwwakeup(tp); /* Bruce???? */ } } #if 1 /* XXX: this doesn't work right yet.. */ /* XXX: this may have been failing because we used to call l_rint() * while we were looping based on these two counters. Now, we collect * the data and then loop stuffing it into l_rint(), making this * useless. Should we cause this to blow away the staging buffer? */ if (rw & FREAD) { ccbp->hi_rxopos = ccbp->hi_rxipos; } #endif } /* * Issue a command to the host card CPU. */ static void si_command(struct si_port *pp, int cmd, int waitflag) { int oldspl; volatile struct si_channel *ccbp = pp->sp_ccb; int x; DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n", pp, cmd, waitflag, ccbp->hi_stat)); oldspl = spltty(); /* Keep others out */ /* wait until it's finished what it was doing.. */ /* XXX: sits in IDLE_BREAK until something disturbs it or break * is turned off. */ while((x = ccbp->hi_stat) != IDLE_OPEN && x != IDLE_CLOSE && x != IDLE_BREAK && x != cmd) { if (in_intr) { /* Prevent sleep in intr */ DPRINT((pp, DBG_PARAM, "cmd intr collision - completing %d\trequested %d\n", x, cmd)); splx(oldspl); return; } else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, "sicmd1", 1)) { splx(oldspl); return; } } /* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */ /* if there was a pending command, cause a state-change wakeup */ switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case CONFIG: case SBREAK: case EBREAK: wakeup(&pp->sp_state); break; default: break; } pp->sp_pend = cmd; /* New command pending */ ccbp->hi_stat = cmd; /* Post it */ if (waitflag) { if (in_intr) { /* If in interrupt handler */ DPRINT((pp, DBG_PARAM, "attempt to sleep in si_intr - cmd req %d\n", cmd)); splx(oldspl); return; } else while(ccbp->hi_stat != IDLE_OPEN && ccbp->hi_stat != IDLE_BREAK) { if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, "sicmd2", 0)) break; } } splx(oldspl); } #ifdef SI_DEBUG void si_dprintf(struct si_port *pp, int flags, const char *fmt, ...) { va_list ap; if ((pp == NULL && (si_debug&flags)) || (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) { if (pp != NULL) printf("%ci%d(%d): ", 's', (int)SI_CARD(minor(pp->sp_tty->t_dev)), (int)SI_PORT(minor(pp->sp_tty->t_dev))); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } } static char * si_mctl2str(enum si_mctl cmd) { switch (cmd) { case GET: return("GET"); case SET: return("SET"); case BIS: return("BIS"); case BIC: return("BIC"); } return("BAD"); } #endif /* DEBUG */ static char * si_modulename(int host_type, int uart_type) { switch (host_type) { /* Z280 based cards */ case SIEISA: case SIHOST2: case SIHOST: case SIPCI: switch (uart_type) { case 0: return(" (XIO)"); case 1: return(" (SI)"); } break; /* T225 based hosts */ case SIJETPCI: case SIJETISA: switch (uart_type) { case 0: return(" (SI)"); case 40: return(" (XIO)"); case 72: return(" (SXDC)"); } break; } return(""); } Index: head/sys/dev/sio/sio.c =================================================================== --- head/sys/dev/sio/sio.c (revision 130343) +++ head/sys/dev/sio/sio.c (revision 130344) @@ -1,3232 +1,3232 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * 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. * 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. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 * from: i386/isa sio.c,v 1.234 */ #include __FBSDID("$FreeBSD$"); #include "opt_comconsole.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_sio.h" /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if DDB > 0 #include #endif #include #include #include #include #ifdef COM_ESP #include #endif #include #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_TO_UNIT(mynor) ((((mynor) & ~0xffffU) >> (8 + 3)) \ | ((mynor) & 0x1f)) #define UNIT_TO_MINOR(unit) ((((unit) & ~0x1fU) << (8 + 3)) \ | ((unit) & 0x1f)) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(flags) ((flags) & 0x01) #define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) #define COM_NOTAST4(flags) ((flags) & 0x04) #else #define COM_ISMULTIPORT(flags) (0) #endif /* COM_MULTIPORT */ #define COM_C_IIR_TXRDYBUG 0x80000 #define COM_CONSOLE(flags) ((flags) & 0x10) #define COM_DEBUGGER(flags) ((flags) & 0x80) #define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) #define COM_FORCECONSOLE(flags) ((flags) & 0x20) #define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) #define COM_LLCONSOLE(flags) ((flags) & 0x40) #define COM_LOSESOUTINTS(flags) ((flags) & 0x08) #define COM_NOFIFO(flags) ((flags) & 0x02) #define COM_NOPROBE(flags) ((flags) & 0x40000) #define COM_NOSCR(flags) ((flags) & 0x100000) #define COM_PPSCTS(flags) ((flags) & 0x10000) #define COM_ST16650A(flags) ((flags) & 0x20000) #define COM_TI16754(flags) ((flags) & 0x200000) #define sio_getreg(com, off) \ (bus_space_read_1((com)->bst, (com)->bsh, (off))) #define sio_setreg(com, off, value) \ (bus_space_write_1((com)->bst, (com)->bsh, (off), (value))) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * comstop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ bool_t st16650a; /* nonzero if Startech 16650A compatible */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int flags; /* copy of device flags */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ibufold; /* old input buffer, to be freed */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ int ibufsize; /* size of ibuf (not include error bytes) */ int ierroff; /* offset of error bytes in ibuf */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ bus_space_tag_t bst; bus_space_handle_t bsh; Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_ctl_port; Port_t int_id_port; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; struct pps_state pps; int pps_bit; #ifdef ALT_BREAK_TO_DEBUGGER int alt_brk_state; #endif u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; u_long rclk; struct resource *irqres; struct resource *ioportres; int ioportrid; void *cookie; dev_t devs[6]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; }; #ifdef COM_ESP static int espattach(struct com_s *com, Port_t esp_port); #endif static timeout_t siobusycheck; static u_int siodivisor(u_long rclk, speed_t speed); static timeout_t siodtrwakeup; static void comhardclose(struct com_s *com); static void sioinput(struct com_s *com); static void siointr1(struct com_s *com); static void siointr(void *arg); static int commctl(struct com_s *com, int bits, int how); static int comparam(struct tty *tp, struct termios *t); static void siopoll(void *); static void siosettimeout(void); static int siosetwater(struct com_s *com, speed_t speed); static void comstart(struct tty *tp); static void comstop(struct tty *tp, int rw); static timeout_t comwakeup; char sio_driver_name[] = "sio"; static struct mtx sio_lock; static int sio_inited; /* table and macro for fast conversion from a unit number to its com struct */ devclass_t sio_devclass; #define com_addr(unit) ((struct com_s *) \ devclass_get_softc(sio_devclass, unit)) /* XXX */ static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static struct cdevsw sio_cdevsw = { .d_version = D_VERSION, .d_open = sioopen, .d_close = sioclose, .d_read = sioread, .d_write = siowrite, .d_ioctl = sioioctl, .d_name = sio_driver_name, .d_flags = D_TTY | D_NEEDGIANT, }; int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; static u_long comdefaultrclk = DEFAULT_RCLK; SYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, ""); static speed_t gdbdefaultrate = GDBSPEED; SYSCTL_UINT(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW, &gdbdefaultrate, GDBSPEED, ""); static u_int com_events; /* input chars + weighted output completions */ static Port_t siocniobase; static int siocnunit = -1; static Port_t siogdbiobase; static int siogdbunit = -1; static void *sio_slow_ih; static void *sio_fast_ih; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); static int sio_numunits; #ifdef COM_ESP /* XXX configure this properly. */ /* XXX quite broken for new-bus. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif /* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */ static int sysctl_machdep_comdefaultrate(SYSCTL_HANDLER_ARGS) { int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (com == NULL) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error; } SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", ""); /* TUNABLE_INT("machdep.conspeed", &comdefaultrate); */ #define SET_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) | (bit)) #define CLR_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) & ~(bit)) /* * Unload the driver and clear the table. * XXX this is mostly wrong. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a kldunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ int siodetach(dev) device_t dev; { struct com_s *com; int i; com = (struct com_s *) device_get_softc(dev); if (com == NULL) { device_printf(dev, "NULL com in siounload\n"); return (0); } com->gone = TRUE; for (i = 0 ; i < 6; i++) destroy_dev(com->devs[i]); if (com->irqres) { bus_teardown_intr(dev, com->irqres, com->cookie); bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres); } if (com->ioportres) bus_release_resource(dev, SYS_RES_IOPORT, com->ioportrid, com->ioportres); if (com->tp && (com->tp->t_state & TS_ISOPEN)) { device_printf(dev, "still open, forcing close\n"); ttyld_close(com->tp, 0); ttyclose(com->tp); } else { if (com->ibuf != NULL) free(com->ibuf, M_DEVBUF); device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (0); } int sioprobe(dev, xrid, rclk, noprobe) device_t dev; int xrid; u_long rclk; int noprobe; { #if 0 static bool_t already_init; device_t xdev; #endif struct com_s *com; u_int divisor; bool_t failures[10]; int fn; device_t idev; Port_t iobase; intrmask_t irqmap[4]; intrmask_t irqs; u_char mcr_image; int result; u_long xirq; u_int flags = device_get_flags(dev); int rid; struct resource *port; rid = xrid; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_COMSIZE, RF_ACTIVE); if (!port) return (ENXIO); com = malloc(sizeof(*com), M_DEVBUF, M_NOWAIT | M_ZERO); if (com == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return (ENOMEM); } device_set_softc(dev, com); com->bst = rman_get_bustag(port); com->bsh = rman_get_bushandle(port); if (rclk == 0) rclk = DEFAULT_RCLK; com->rclk = rclk; while (sio_inited != 2) if (atomic_cmpset_int(&sio_inited, 0, 1)) { mtx_init(&sio_lock, sio_driver_name, NULL, (comconsole != -1) ? MTX_SPIN | MTX_QUIET : MTX_SPIN); atomic_store_rel_int(&sio_inited, 2); } #if 0 /* * XXX this is broken - when we are first called, there are no * previously configured IO ports. We could hard code * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse. * This code has been doing nothing since the conversion since * "count" is zero the first time around. */ if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ device_t *devs; int count, i, xioport; devclass_get_devices(sio_devclass, &devs, &count); for (i = 0; i < count; i++) { xdev = devs[i]; if (device_is_enabled(xdev) && bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport, NULL) == 0) outb(xioport + com_mcr, 0); } free(devs, M_TEMP); already_init = TRUE; } #endif if (COM_LLCONSOLE(flags)) { printf("sio%d: reserved for low-level i/o\n", device_get_unit(dev)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { Port_t xiobase; u_long io; idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", device_get_unit(dev), COM_MPMASTER(flags)); idev = dev; } if (!COM_NOTAST4(flags)) { if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io, NULL) == 0) { xiobase = io; if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) == 0) outb(xiobase + com_scr, 0x80); else outb(xiobase + com_scr, 0); } mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = rman_get_start(port); /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ mtx_lock_spin(&sio_lock); /* EXTRA DELAY? */ /* * For the TI16754 chips, set prescaler to 1 (4 is often the * default after-reset value) as otherwise it's impossible to * get highest baudrates. */ if (COM_TI16754(flags)) { u_char cfcr, efr; cfcr = sio_getreg(com, com_cfcr); sio_setreg(com, com_cfcr, CFCR_EFR_ENABLE); efr = sio_getreg(com, com_efr); /* Unlock extended features to turn off prescaler. */ sio_setreg(com, com_efr, efr | EFR_EFE); /* Disable EFR. */ sio_setreg(com, com_cfcr, (cfcr != CFCR_EFR_ENABLE) ? cfcr : 0); /* Turn off prescaler. */ sio_setreg(com, com_mcr, sio_getreg(com, com_mcr) & ~MCR_PRESCALE); sio_setreg(com, com_cfcr, CFCR_EFR_ENABLE); sio_setreg(com, com_efr, efr); sio_setreg(com, com_cfcr, cfcr); } /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ if (iobase == siocniobase) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS); divisor = siodivisor(rclk, SIO_TEST_SPEED); sio_setreg(com, com_dlbl, divisor & 0xff); sio_setreg(com, com_dlbh, divisor >> 8); sio_setreg(com, com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image); sio_setreg(com, com_ier, 0); DELAY(1000); /* XXX */ irqmap[0] = isa_irq_pending(); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ sio_setreg(com, com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ sio_setreg(com, com_data, 0); if (iobase == siocniobase) DELAY((1 + 2) * 1000000 / (comdefaultrate / 10)); else DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image); /* * It seems my Xircom CBEM56G Cardbus modem wants to be reset * to 8 bits *again*, or else probe test 0 will fail. * gwk@sgi.com, 4/19/2001 */ sio_setreg(com, com_cfcr, CFCR_8BITS); /* * Some PCMCIA cards (Palido 321s, DC-1S, ...) have the "TXRDY bug", * so we probe for a buggy IIR_TXRDY implementation even in the * noprobe case. We don't probe for it in the !noprobe case because * noprobe is always set for PCMCIA cards and the problem is not * known to affect any other cards. */ if (noprobe) { /* Read IIR a few times. */ for (fn = 0; fn < 2; fn ++) { DELAY(10000); failures[6] = sio_getreg(com, com_iir); } /* IIR_TXRDY should be clear. Is it? */ result = 0; if (failures[6] & IIR_TXRDY) { /* * No. We seem to have the bug. Does our fix for * it work? */ sio_setreg(com, com_ier, 0); if (sio_getreg(com, com_iir) & IIR_NOPEND) { /* Yes. We discovered the TXRDY bug! */ SET_FLAG(dev, COM_C_IIR_TXRDYBUG); } else { /* No. Just fail. XXX */ result = ENXIO; sio_setreg(com, com_mcr, 0); } } else { /* Yes. No bug. */ CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); } sio_setreg(com, com_ier, 0); sio_setreg(com, com_cfcr, CFCR_8BITS); mtx_unlock_spin(&sio_lock); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); if (iobase == siocniobase) result = 0; if (result != 0) { device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (result); } /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS; failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY; failures[2] = sio_getreg(com, com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ irqmap[1] = isa_irq_pending(); failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY; DELAY(1000); /* XXX */ irqmap[2] = isa_irq_pending(); failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving it) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ sio_setreg(com, com_ier, 0); sio_setreg(com, com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = sio_getreg(com, com_ier); DELAY(1000); /* XXX */ irqmap[3] = isa_irq_pending(); failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; mtx_unlock_spin(&sio_lock); irqs = irqmap[1] & ~irqmap[0]; if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 && ((1 << xirq) & irqs) == 0) { printf( "sio%d: configured irq %ld not in bitmap of probed irqs %#x\n", device_get_unit(dev), xirq, irqs); printf( "sio%d: port may not be enabled\n", device_get_unit(dev)); } if (bootverbose) printf("sio%d: irq maps: %#x %#x %#x %#x\n", device_get_unit(dev), irqmap[0], irqmap[1], irqmap[2], irqmap[3]); result = 0; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { sio_setreg(com, com_mcr, 0); result = ENXIO; if (bootverbose) { printf("sio%d: probe failed test(s):", device_get_unit(dev)); for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) printf(" %d", fn); printf("\n"); } break; } bus_release_resource(dev, SYS_RES_IOPORT, rid, port); if (iobase == siocniobase) result = 0; if (result != 0) { device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (result); } #ifdef COM_ESP static int espattach(com, esp_port) struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); /* * Bits 0,1 of dips say which COM port we are. */ if (rman_get_start(com->ioportres) == likely_com_ports[dips & 0x03]) printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ int sioattach(dev, xrid, rclk) device_t dev; int xrid; u_long rclk; { struct com_s *com; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int minorbase; int unit; u_int flags; int rid; struct resource *port; int ret; rid = xrid; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_COMSIZE, RF_ACTIVE); if (!port) return (ENXIO); iobase = rman_get_start(port); unit = device_get_unit(dev); com = device_get_softc(dev); flags = device_get_flags(dev); if (unit >= sio_numunits) sio_numunits = unit + 1; /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->ioportres = port; com->ioportrid = rid; com->bst = rman_get_bustag(port); com->bsh = rman_get_bushandle(port); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(flags) != 0; com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; com->tx_fifo_size = 1; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->data_port = iobase + com_data; com->int_ctl_port = iobase + com_ier; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; if (rclk == 0) rclk = DEFAULT_RCLK; com->rclk = rclk; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; com->lt_out.c_ispeed = com->lt_out.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; if (siosetwater(com, com->it_in.c_ispeed) != 0) { mtx_unlock_spin(&sio_lock); /* * Leave i/o resources allocated if this is a `cn'-level * console, so that other devices can't snarf them. */ if (iobase != siocniobase) bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return (ENOMEM); } mtx_unlock_spin(&sio_lock); termioschars(&com->it_in); com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags) && !COM_NOSCR(flags)) { u_char scr; u_char scr1; u_char scr2; scr = sio_getreg(com, com_scr); sio_setreg(com, com_scr, 0xa5); scr1 = sio_getreg(com, com_scr); sio_setreg(com, com_scr, 0x5a); scr2 = sio_getreg(com, com_scr); sio_setreg(com, com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250 or not responding"); goto determined_type; } } sio_setreg(com, com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: if (COM_NOFIFO(flags)) { printf(" 16550A fifo disabled"); break; } com->hasfifo = TRUE; if (COM_ST16650A(flags)) { printf(" ST16650A"); com->st16650a = TRUE; com->tx_fifo_size = 32; break; } if (COM_TI16754(flags)) { printf(" TI16754"); com->tx_fifo_size = 64; break; } printf(" 16550A"); #ifdef COM_ESP for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(com, *espp)) { com->tx_fifo_size = 1024; break; } if (com->esp) break; #endif com->tx_fifo_size = COM_FIFOSIZE(flags); if (com->tx_fifo_size == 0) com->tx_fifo_size = 16; else printf(" lookalike with %u bytes FIFO", com->tx_fifo_size); break; } #ifdef COM_ESP if (com->esp) { /* * Set 16550 compatibility mode. * We don't use the ESP_MODE_SCALE bit to increase the * fifo trigger levels because we can't handle large * bursts of input. * XXX flow control should be set in comparam(), not here. */ outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); /* Set RTS/CTS flow control. */ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); /* Set flow-control levels. */ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); } #endif /* COM_ESP */ sio_setreg(com, com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { device_t masterdev; com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(flags)) printf(" master"); printf(")"); masterdev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); com->no_irq = (masterdev == NULL || bus_get_resource(masterdev, SYS_RES_IRQ, 0, NULL, NULL) != 0); } #endif /* COM_MULTIPORT */ if (unit == comconsole) printf(", console"); if (COM_IIR_TXRDYBUG(flags)) printf(" with a buggy IIR_TXRDY implementation"); printf("\n"); if (sio_fast_ih == NULL) { swi_add(&tty_ithd, "tty:sio", siopoll, NULL, SWI_TTY, 0, &sio_fast_ih); swi_add(&clk_ithd, "tty:sio", siopoll, NULL, SWI_TTY, 0, &sio_slow_ih); } minorbase = UNIT_TO_MINOR(unit); com->devs[0] = make_dev(&sio_cdevsw, minorbase, UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); com->devs[1] = make_dev(&sio_cdevsw, minorbase | CONTROL_INIT_STATE, UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); com->devs[2] = make_dev(&sio_cdevsw, minorbase | CONTROL_LOCK_STATE, UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); com->devs[3] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK, UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); com->devs[4] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_INIT_STATE, UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); com->devs[5] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_LOCK_STATE, UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); for (rid = 0; rid < 6; rid++) com->devs[rid]->si_drv1 = com; com->flags = flags; com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; if (COM_PPSCTS(flags)) com->pps_bit = MSR_CTS; else com->pps_bit = MSR_DCD; pps_init(&com->pps); rid = 0; com->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (com->irqres) { ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, INTR_TYPE_TTY | INTR_FAST, siointr, com, &com->cookie); if (ret) { ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, INTR_TYPE_TTY, siointr, com, &com->cookie); if (ret == 0) device_printf(dev, "unable to activate interrupt in fast mode - using normal mode\n"); } if (ret) device_printf(dev, "could not activate interrupt\n"); #if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ defined(ALT_BREAK_TO_DEBUGGER)) /* * Enable interrupts for early break-to-debugger support * on the console. */ if (ret == 0 && unit == comconsole) outb(siocniobase + com_ier, IER_ERXRDY | IER_ERLS | IER_EMSC); #endif } return (0); } static int sioopen(dev, flag, mode, td) dev_t dev; int flag; int mode; struct thread *td; { struct com_s *com; int error; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); if (com == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); tp = dev->si_tty = com->tp = ttymalloc(com->tp); s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && suser(td)) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_stop = comstop; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ if (com->hasfifo) { int i; /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ for (i = 0; i < 500; i++) { sio_setreg(com, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); /* * XXX the delays are for superstitious * historical reasons. It must be less than * the character time at the maximum * supported speed (87 usec at 115200 bps * 8N1). Otherwise we might loop endlessly * if data is streaming in. We used to use * delays of 100. That usually worked * because DELAY(100) used to usually delay * for about 85 usec instead of 100. */ DELAY(50); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; sio_setreg(com, com_fifo, 0); DELAY(50); (void) inb(com->data_port); } if (i == 500) { error = EIO; goto out; } } mtx_lock_spin(&sio_lock); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(com->int_ctl_port, IER_ERXRDY | IER_ERLS | IER_EMSC | (COM_IIR_TXRDYBUG(com->flags) ? 0 : IER_ETXRDY)); mtx_unlock_spin(&sio_lock); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) ttyld_modem(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = ttyld_open(tp, dev); com->hotchar = ttyldoptim(tp); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, td) dev_t dev; int flag; int mode; struct thread *td; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL) return (ENODEV); tp = com->tp; s = spltty(); ttyld_close(tp, flag); com->hotchar = ttyldoptim(tp); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); if (com->ibuf != NULL) free(com->ibuf, M_DEVBUF); bzero(tp, sizeof *tp); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { int s; struct tty *tp; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = FALSE; com->do_dcd_timestamp = FALSE; com->pps.ppsparam.mode = 0; sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); tp = com->tp; #if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ defined(ALT_BREAK_TO_DEBUGGER)) /* * Leave interrupts enabled and don't clear DTR if this is the * console. This allows us to detect break-to-debugger events * while the console device is closed. */ if (com->unit != comconsole) #endif { sio_setreg(com, com_ier, 0); if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || (!com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ sio_setreg(com, com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct com_s *com; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL || com->gone) return (ENODEV); return (ttyld_read(com->tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct com_s *com; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); if (com == NULL || com->gone) return (ENODEV); /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return (ttyld_write(com->tp, uio, flag)); } static void siobusycheck(chan) void *chan; { struct com_s *com; int s; com = (struct com_s *)chan; /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. * If siointr1() clears CS_BUSY after we look at it, then we'll get * called again. Reading the line status port outside of siointr1() * is safe because CS_BUSY is clear so there are no output interrupts * to lose. */ s = spltty(); if (com->state & CS_BUSY) com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY)) { com->tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); com->extra_state &= ~CSE_BUSYCHECK; } else timeout(siobusycheck, com, hz / 100); splx(s); } static u_int siodivisor(rclk, speed) u_long rclk; speed_t speed; { long actual_speed; u_int divisor; int error; if (speed == 0) return (0); #if UINT_MAX > (ULONG_MAX - 1) / 8 if (speed > (ULONG_MAX - 1) / 8) return (0); #endif divisor = (rclk / (8UL * speed) + 1) / 2; if (divisor == 0 || divisor >= 65536) return (0); actual_speed = rclk / (16UL * divisor); /* 10 times error in percent: */ error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2; /* 3.0% maximum error tolerance: */ if (error < -30 || error > 30) return (0); return (divisor); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } /* * Call this function with the sio_lock mutex held. It will return with the * lock still held. */ static void sioinput(com) struct com_s *com; { u_char *buf; int incc; u_char line_status; int recv_data; struct tty *tp; buf = com->ibuf; tp = com->tp; if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; return; } if (tp->t_state & TS_CAN_BYPASS_L_RINT) { /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ do { /* * This may look odd, but it is using save-and-enable * semantics instead of the save-and-disable semantics * that are used everywhere else. */ mtx_unlock_spin(&sio_lock); incc = com->iptr - buf; if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); buf += incc; tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } mtx_lock_spin(&sio_lock); } while (buf < com->iptr); } else { do { /* * This may look odd, but it is using save-and-enable * semantics instead of the save-and-disable semantics * that are used everywhere else. */ mtx_unlock_spin(&sio_lock); line_status = buf[com->ierroff]; recv_data = *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } ttyld_rint(tp, recv_data); mtx_lock_spin(&sio_lock); } while (buf < com->iptr); } com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; /* * There is now room for another low-level buffer full of input, * so enable RTS if it is now disabled and there is room in the * high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } static void siointr(arg) void *arg; { struct com_s *com; #ifndef COM_MULTIPORT com = (struct com_s *)arg; mtx_lock_spin(&sio_lock); siointr1(com); mtx_unlock_spin(&sio_lock); #else /* COM_MULTIPORT */ bool_t possibly_more_intrs; int unit; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ mtx_lock_spin(&sio_lock); do { possibly_more_intrs = FALSE; for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); /* * XXX COM_LOCK(); * would it work here, or be counter-productive? */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); mtx_unlock_spin(&sio_lock); #endif /* COM_MULTIPORT */ } static struct timespec siots[8]; static int siotso; static int volatile siotsunit = -1; static int sysctl_siots(SYSCTL_HANDLER_ARGS) { char buf[128]; long long delta; size_t len; int error, i, tso; for (i = 1, tso = siotso; i < tso; i++) { delta = (long long)(siots[i].tv_sec - siots[i - 1].tv_sec) * 1000000000 + (siots[i].tv_nsec - siots[i - 1].tv_nsec); len = sprintf(buf, "%lld\n", delta); if (delta >= 110000) len += sprintf(buf + len - 1, ": *** %ld.%09ld\n", (long)siots[i].tv_sec, siots[i].tv_nsec) - 1; if (i == tso - 1) buf[len - 1] = '\0'; error = SYSCTL_OUT(req, buf, len); if (error != 0) return (error); uio_yield(); } return (0); } SYSCTL_PROC(_machdep, OID_AUTO, siots, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_siots, "A", "sio timestamps"); static void siointr1(com) struct com_s *com; { u_char int_ctl; u_char int_ctl_new; u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; if (COM_IIR_TXRDYBUG(com->flags)) { int_ctl = inb(com->int_ctl_port); int_ctl_new = int_ctl; } else { int_ctl = 0; int_ctl_new = 0; } while (!com->gone) { if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) { modem_status = inb(com->modem_status_port); if ((modem_status ^ com->last_modem_status) & com->pps_bit) { pps_capture(&com->pps); pps_event(&com->pps, (modem_status & com->pps_bit) ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); } } line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); #ifdef DDB #ifdef ALT_BREAK_TO_DEBUGGER if (com->unit == comconsole && db_alt_break(recv_data, &com->alt_brk_state) != 0) breakpoint(); #endif /* ALT_BREAK_TO_DEBUGGER */ #endif /* DDB */ if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { /* * Don't store BI if IGNBRK or FE/PE if IGNPAR. * Otherwise, push the work to a higher level * (to handle PARMRK) if we're bypassing. * Otherwise, convert BI/FE and PE+INPCK to 0. * * This makes bypassing work right in the * usual "raw" case (IGNBRK set, and IGNPAR * and INPCK clear). * * Note: BI together with FE/PE means just BI. */ if (line_status & LSR_BI) { #if defined(DDB) && defined(BREAK_TO_DEBUGGER) if (com->unit == comconsole) { breakpoint(); goto cont; } #endif if (com->tp == NULL || com->tp->t_iflag & IGNBRK) goto cont; } else { if (com->tp == NULL || com->tp->t_iflag & IGNPAR) goto cont; } if (com->tp->t_state & TS_CAN_BYPASS_L_RINT && (line_status & (LSR_BI | LSR_FE) || com->tp->t_iflag & INPCK)) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) swi_sched(sio_fast_ih, 0); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; swi_sched(sio_slow_ih, SWI_DELAY); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) swi_sched(sio_fast_ih, 0); #endif ioptr[0] = recv_data; ioptr[com->ierroff] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) goto txrdy; /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; swi_sched(sio_fast_ih, 0); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } txrdy: /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1 && com->unit != siotsunit) { u_int ocount; ocount = com->obufq.l_tail - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; if (com->unit == siotsunit && siotso < sizeof siots / sizeof siots[0]) nanouptime(&siots[siotso++]); } com->obufq.l_head = ioptr; if (COM_IIR_TXRDYBUG(com->flags)) int_ctl_new = int_ctl | IER_ETXRDY; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ if (COM_IIR_TXRDYBUG(com->flags)) int_ctl_new = int_ctl & ~IER_ETXRDY; com->state &= ~CS_BUSY; } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; /* handle at high level ASAP */ swi_sched(sio_fast_ih, 0); } } if (COM_IIR_TXRDYBUG(com->flags) && int_ctl != int_ctl_new) outb(com->int_ctl_port, int_ctl_new); } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, td) dev_t dev; u_long cmd; caddr_t data; int flag; struct thread *td; { struct com_s *com; int error; int mynor; int s; struct tty *tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL || com->gone) return (ENODEV); if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(td); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } tp = com->tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = ttyioctl(dev, cmd, data, flag, td); com->hotchar = ttyldoptim(tp); if (error != ENOTTY) return (error); s = spltty(); switch (cmd) { case TIOCSBRK: sio_setreg(com, com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(td); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); error = pps_ioctl(cmd, data, &com->pps); if (error == ENODEV) error = ENOTTY; return (error); } splx(s); return (0); } /* software interrupt handler for SWI_TTY */ static void siopoll(void *dummy) { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < sio_numunits; ++unit) { struct com_s *com; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL || com->gone) { /* * Discard any events related to never-opened or * going-away devices. */ mtx_lock_spin(&sio_lock); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; mtx_unlock_spin(&sio_lock); continue; } if (com->iptr != com->ibuf) { mtx_lock_spin(&sio_lock); sioinput(com); mtx_unlock_spin(&sio_lock); } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; mtx_lock_spin(&sio_lock); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; mtx_unlock_spin(&sio_lock); if (delta_modem_status & MSR_DCD) ttyld_modem(tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { mtx_lock_spin(&sio_lock); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; mtx_unlock_spin(&sio_lock); if (!(com->state & CS_BUSY) && !(com->extra_state & CSE_BUSYCHECK)) { timeout(siobusycheck, com, hz / 100); com->extra_state |= CSE_BUSYCHECK; } ttyld_start(tp); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; u_int divisor; u_char dlbh; u_char dlbl; u_char efr_flowbits; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); if (com == NULL) return (ENODEV); /* check requested parameters */ if (t->c_ispeed != (t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed)) return (EINVAL); divisor = siodivisor(com->rclk, t->c_ispeed); if (divisor == 0) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ s = spltty(); if (t->c_ospeed == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. * * The fifo trigger level cannot be set at RX_HIGH for high * speed connections without further work on reducing * interrupt disablement times in other parts of the system, * without producing silo overflow errors. */ com->fifo_image = com->unit == siotsunit ? 0 : t->c_ispeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_MEDH; #ifdef COM_ESP /* * The Hayes ESP card needs the fifo DMA mode bit set * in compatibility mode. If not, it will interrupt * for each character received. */ if (com->esp) com->fifo_image |= FIFO_DMA_MODE; #endif sio_setreg(com, com_fifo, com->fifo_image); } /* * This returns with interrupts disabled so that we can complete * the speed change atomically. Keeping interrupts disabled is * especially important while com_data is hidden. */ (void) siosetwater(com, t->c_ispeed); sio_setreg(com, com_cfcr, cfcr | CFCR_DLAB); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (UMC8669F), setting them while input * is arriving loses sync until data stops arriving. */ dlbl = divisor & 0xFF; if (sio_getreg(com, com_dlbl) != dlbl) sio_setreg(com, com_dlbl, dlbl); dlbh = divisor >> 8; if (sio_getreg(com, com_dlbh) != dlbh) sio_setreg(com, com_dlbh, dlbh); efr_flowbits = 0; if (cflag & CRTS_IFLOW) { com->state |= CS_RTS_IFLOW; efr_flowbits |= EFR_AUTORTS; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; efr_flowbits |= EFR_AUTOCTS; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } if (com->st16650a) { sio_setreg(com, com_lcr, LCR_EFR_ENABLE); sio_setreg(com, com_efr, (sio_getreg(com, com_efr) & ~(EFR_AUTOCTS | EFR_AUTORTS)) | efr_flowbits); } sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); /* XXX shouldn't call functions while intrs are disabled. */ com->hotchar = ttyldoptim(tp); mtx_unlock_spin(&sio_lock); splx(s); comstart(tp); if (com->ibufold != NULL) { free(com->ibufold, M_DEVBUF); com->ibufold = NULL; } return (0); } /* * This function must be called with the sio_lock mutex released and will * return with it obtained. */ static int siosetwater(com, speed) struct com_s *com; speed_t speed; { int cp4ticks; u_char *ibuf; int ibufsize; struct tty *tp; /* * Make the buffer size large enough to handle a softtty interrupt * latency of about 2 ticks without loss of throughput or data * (about 3 ticks if input flow control is not used or not honoured, * but a bit less for CS5-CS7 modes). */ cp4ticks = speed / 10 / hz * 4; for (ibufsize = 128; ibufsize < cp4ticks;) ibufsize <<= 1; if (ibufsize == com->ibufsize) { mtx_lock_spin(&sio_lock); return (0); } /* * Allocate input buffer. The extra factor of 2 in the size is * to allow for an error byte for each input byte. */ ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); if (ibuf == NULL) { mtx_lock_spin(&sio_lock); return (ENOMEM); } /* Initialize non-critical variables. */ com->ibufold = com->ibuf; com->ibufsize = ibufsize; tp = com->tp; if (tp != NULL) { tp->t_ififosize = 2 * ibufsize; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; } /* * Read current input buffer, if any. Continue with interrupts * disabled. */ mtx_lock_spin(&sio_lock); if (com->iptr != com->ibuf) sioinput(com); /*- * Initialize critical variables, including input buffer watermarks. * The external device is asked to stop sending when the buffer * exactly reaches high water, or when the high level requests it. * The high level is notified immediately (rather than at a later * clock tick) when this watermark is reached. * The buffer size is chosen so the watermark should almost never * be reached. * The low watermark is invisibly 0 since the buffer is always * emptied all at once. */ com->iptr = com->ibuf = ibuf; com->ibufend = ibuf + ibufsize; com->ierroff = ibufsize; com->ihighwater = ibuf + 3 * ibufsize / 4; return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); if (com == NULL) return; s = spltty(); mtx_lock_spin(&sio_lock); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } mtx_unlock_spin(&sio_lock); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; mtx_lock_spin(&sio_lock); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } mtx_unlock_spin(&sio_lock); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; mtx_lock_spin(&sio_lock); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } mtx_unlock_spin(&sio_lock); } tp->t_state |= TS_BUSY; } mtx_lock_spin(&sio_lock); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ mtx_unlock_spin(&sio_lock); ttwwakeup(tp); splx(s); } static void comstop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com == NULL || com->gone) return; mtx_lock_spin(&sio_lock); if (rw & FWRITE) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif sio_setreg(com, com_fifo, FIFO_XMT_RST | com->fifo_image); com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif sio_setreg(com, com_fifo, FIFO_RCV_RST | com->fifo_image); com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } mtx_unlock_spin(&sio_lock); comstart(tp); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); mtx_lock_spin(&sio_lock); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } mtx_unlock_spin(&sio_lock); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { mtx_lock_spin(&sio_lock); siointr1(com); mtx_unlock_spin(&sio_lock); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < sio_numunits; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; mtx_lock_spin(&sio_lock); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; mtx_unlock_spin(&sio_lock); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } /* * Following are all routines needed for SIO to act as console */ struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; /* * This is a function in order to not replicate "ttyd%d" more * places than absolutely necessary. */ static void siocnset(struct consdev *cd, int unit) { cd->cn_unit = unit; sprintf(cd->cn_name, "ttyd%d", unit); } #ifndef __alpha__ static speed_t siocngetspeed(Port_t, u_long rclk); #endif static void siocnclose(struct siocnstate *sp, Port_t iobase); static void siocnopen(struct siocnstate *sp, Port_t iobase, int speed); static void siocntxwait(Port_t iobase); #ifdef __alpha__ int siocnattach(int port, int speed); int siogdbattach(int port, int speed); int siogdbgetc(void); void siogdbputc(int c); #else static cn_probe_t siocnprobe; static cn_init_t siocninit; static cn_term_t siocnterm; #endif static cn_checkc_t siocncheckc; static cn_getc_t siocngetc; static cn_putc_t siocnputc; #ifndef __alpha__ CONS_DRIVER(sio, siocnprobe, siocninit, siocnterm, siocngetc, siocncheckc, siocnputc, NULL); #endif #if DDB > 0 static struct consdev gdbconsdev; #endif static void siocntxwait(iobase) Port_t iobase; { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } #ifndef __alpha__ /* * Read the serial port specified and try to figure out what speed * it's currently running at. We're assuming the serial port has * been initialized and is basicly idle. This routine is only intended * to be run at system startup. * * If the value read from the serial port doesn't make sense, return 0. */ static speed_t siocngetspeed(iobase, rclk) Port_t iobase; u_long rclk; { u_int divisor; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(iobase + com_dlbl); dlbh = inb(iobase + com_dlbh); outb(iobase + com_cfcr, cfcr); divisor = dlbh << 8 | dlbl; /* XXX there should be more sanity checking. */ if (divisor == 0) return (CONSPEED); return (rclk / (16UL * divisor)); } #endif static void siocnopen(sp, iobase, speed) struct siocnstate *sp; Port_t iobase; int speed; { u_int divisor; u_char dlbh; u_char dlbl; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(iobase); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (Startech), setting them clears the * data input register. This also reduces the effects of the * UMC8669F bug. */ divisor = siodivisor(comdefaultrclk, speed); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp, iobase) struct siocnstate *sp; Port_t iobase; { /* * Restore the device control registers. */ siocntxwait(iobase); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); if (sp->dlbl != inb(iobase + com_dlbl)) outb(iobase + com_dlbl, sp->dlbl); if (sp->dlbh != inb(iobase + com_dlbh)) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } #ifndef __alpha__ static void siocnprobe(cp) struct consdev *cp; { speed_t boot_speed; u_char cfcr; u_int divisor; int s, unit; struct siocnstate sp; /* * Find our first enabled console, if any. If it is a high-level * console device, then initialize it and return successfully. * If it is a low-level console device, then initialize it and * return unsuccessfully. It must be initialized in both cases * for early use by console drivers and debuggers. Initializing * the hardware is not necessary in all cases, since the i/o * routines initialize it on the fly, but it is necessary if * input might arrive while the hardware is switched back to an * uninitialized state. We can't handle multiple console devices * yet because our low-level routines don't take a device arg. * We trust the user to set the console flags properly so that we * don't need to probe. */ cp->cn_pri = CN_DEAD; for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ int flags; if (resource_disabled("sio", unit)) continue; if (resource_int_value("sio", unit, "flags", &flags)) continue; if (COM_CONSOLE(flags) || COM_DEBUGGER(flags)) { int port; Port_t iobase; if (resource_int_value("sio", unit, "port", &port)) continue; iobase = port; s = spltty(); if (boothowto & RB_SERIAL) { boot_speed = siocngetspeed(iobase, comdefaultrclk); if (boot_speed) comdefaultrate = boot_speed; } /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, comdefaultrate); outb(iobase + com_dlbl, divisor & 0xff); outb(iobase + com_dlbh, divisor >> 8); outb(iobase + com_cfcr, cfcr); siocnopen(&sp, iobase, comdefaultrate); splx(s); if (COM_CONSOLE(flags) && !COM_LLCONSOLE(flags)) { siocnset(cp, unit); cp->cn_pri = COM_FORCECONSOLE(flags) || boothowto & RB_SERIAL ? CN_REMOTE : CN_NORMAL; siocniobase = iobase; siocnunit = unit; } if (COM_DEBUGGER(flags)) { printf("sio%d: gdb debugging port\n", unit); siogdbiobase = iobase; siogdbunit = unit; #if DDB > 0 siocnset(&gdbconsdev, unit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; #endif } } } #ifdef __i386__ #if DDB > 0 /* * XXX Ugly Compatability. * If no gdb port has been specified, set it to be the console * as some configuration files don't specify the gdb port. */ if (gdb_arg == NULL && (boothowto & RB_GDB)) { printf("Warning: no GDB port specified. Defaulting to sio%d.\n", siocnunit); printf("Set flag 0x80 on desired GDB port in your\n"); printf("configuration file (currently sio only).\n"); siogdbiobase = siocniobase; siogdbunit = siocnunit; siocnset(&gdbconsdev, siocnunit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; } #endif #endif } static void siocninit(cp) struct consdev *cp; { comconsole = cp->cn_unit; } static void siocnterm(cp) struct consdev *cp; { comconsole = -1; } #endif #ifdef __alpha__ CONS_DRIVER(sio, NULL, NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL); int siocnattach(port, speed) int port; int speed; { int s; u_char cfcr; u_int divisor; struct siocnstate sp; int unit = 0; /* XXX random value! */ siocniobase = port; siocnunit = unit; comdefaultrate = speed; sio_consdev.cn_pri = CN_NORMAL; siocnset(&sio_consdev, unit); s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, comdefaultrate); outb(siocniobase + com_dlbl, divisor & 0xff); outb(siocniobase + com_dlbh, divisor >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp, siocniobase, comdefaultrate); splx(s); cnadd(&sio_consdev); return (0); } int siogdbattach(port, speed) int port; int speed; { int s; u_char cfcr; u_int divisor; struct siocnstate sp; int unit = 1; /* XXX random value! */ siogdbiobase = port; gdbdefaultrate = speed; printf("sio%d: gdb debugging port\n", unit); siogdbunit = unit; #if DDB > 0 siocnset(&gdbconsdev, unit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; #endif s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siogdbiobase + com_cfcr); outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, gdbdefaultrate); outb(siogdbiobase + com_dlbl, divisor & 0xff); outb(siogdbiobase + com_dlbh, divisor >> 8); outb(siogdbiobase + com_cfcr, cfcr); siocnopen(&sp, siogdbiobase, gdbdefaultrate); splx(s); return (0); } #endif static int siocncheckc(struct consdev *cd) { int c; Port_t iobase; int s; struct siocnstate sp; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = -1; siocnclose(&sp, iobase); splx(s); return (c); } static int siocngetc(struct consdev *cd) { int c; Port_t iobase; int s; struct siocnstate sp; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } static void siocnputc(struct consdev *cd, int c) { int need_unlock; int s; struct siocnstate sp; Port_t iobase; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); need_unlock = 0; if (sio_inited == 2 && !mtx_owned(&sio_lock)) { mtx_lock_spin(&sio_lock); need_unlock = 1; } siocnopen(&sp, iobase, speed); siocntxwait(iobase); outb(iobase + com_data, c); siocnclose(&sp, iobase); if (need_unlock) mtx_unlock_spin(&sio_lock); splx(s); } #ifdef __alpha__ int siogdbgetc() { int c; Port_t iobase; speed_t speed; int s; struct siocnstate sp; if (siogdbunit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } void siogdbputc(c) int c; { Port_t iobase; speed_t speed; int s; struct siocnstate sp; if (siogdbunit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); siocntxwait(siogdbiobase); outb(siogdbiobase + com_data, c); siocnclose(&sp, siogdbiobase); splx(s); } #endif Index: head/sys/dev/sx/sx.c =================================================================== --- head/sys/dev/sx/sx.c (revision 130343) +++ head/sys/dev/sx/sx.c (revision 130344) @@ -1,1992 +1,1992 @@ /* * Device tsfsdriver for Specialix I/O8+ multiport serial card. * * Copyright 2003 Frank Mayhar * * Derived from the "si" driver by Peter Wemm , using * lots of information from the Linux "specialix" driver by Roger Wolff * and from the Intel CD1865 "Intelligent Eight- * Channel Communications Controller" datasheet. Roger was also nice * enough to answer numerous questions about stuff specific to the I/O8+ * not covered by the CD1865 datasheet. * * 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 * notices, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notices, this list of conditions and the foljxowing disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY ``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 AUTHORS BE LIABLE. * * $FreeBSD$ */ /* Main tty driver routines for the Specialix I/O8+ device driver. */ #include "opt_compat.h" #include "opt_debug_sx.h" #include #include -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SX_BROKEN_CTS enum sx_mctl { GET, SET, BIS, BIC }; static int sx_modem(struct sx_softc *, struct sx_port *, enum sx_mctl, int); static void sx_write_enable(struct sx_port *, int); static void sx_start(struct tty *); static void sx_stop(struct tty *, int); static void sxhardclose(struct sx_port *pp); static void sxdtrwakeup(void *chan); static void sx_shutdown_chan(struct sx_port *); #ifdef SX_DEBUG static char *sx_mctl2str(enum sx_mctl cmd); #endif static int sxparam(struct tty *, struct termios *); static void sx_modem_state(struct sx_softc *sc, struct sx_port *pp, int card); static d_open_t sxopen; static d_close_t sxclose; static d_write_t sxwrite; static d_ioctl_t sxioctl; #define CDEV_MAJOR 185 static struct cdevsw sx_cdevsw = { .d_version = D_VERSION, .d_open = sxopen, .d_close = sxclose, .d_write = sxwrite, .d_ioctl = sxioctl, .d_name = "sx", .d_flags = D_TTY | D_NEEDGIANT, }; static int sx_debug = 0; /* DBG_ALL|DBG_PRINTF|DBG_MODEM|DBG_IOCTL|DBG_PARAM;e */ SYSCTL_INT(_machdep, OID_AUTO, sx_debug, CTLFLAG_RW, &sx_debug, 0, ""); static struct tty *sx__tty; static int sx_numunits; devclass_t sx_devclass; /* * See sx.h for these values. */ static struct speedtab bdrates[] = { { B75, CLK75, }, { B110, CLK110, }, { B150, CLK150, }, { B300, CLK300, }, { B600, CLK600, }, { B1200, CLK1200, }, { B2400, CLK2400, }, { B4800, CLK4800, }, { B9600, CLK9600, }, { B19200, CLK19200, }, { B38400, CLK38400, }, { B57600, CLK57600, }, { B115200, CLK115200, }, { -1, -1 }, }; /* * Approximate (rounded) character per second rates. Translated at card * initialization time to characters per clock tick. */ static int done_chartimes = 0; static struct speedtab chartimes[] = { { B75, 8, }, { B110, 11, }, { B150, 15, }, { B300, 30, }, { B600, 60, }, { B1200, 120, }, { B2400, 240, }, { B4800, 480, }, { B9600, 960, }, { B19200, 1920, }, { B38400, 3840, }, { B57600, 5760, }, { B115200, 11520, }, { -1, -1 }, }; static volatile int in_interrupt = 0; /* Inside interrupt handler? */ static int sx_flags; /* The flags we were configured with. */ SYSCTL_INT(_machdep, OID_AUTO, sx_flags, CTLFLAG_RW, &sx_flags, 0, ""); #ifdef POLL static int sx_pollrate; /* in addition to irq */ static int sx_realpoll = 0; /* poll HW on timer */ SYSCTL_INT(_machdep, OID_AUTO, sx_pollrate, CTLFLAG_RW, &sx_pollrate, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, sx_realpoll, CTLFLAG_RW, &sx_realpoll, 0, ""); static int init_finished = 0; static void sx_poll(void *); #endif /* * sxattach() * Initialize and attach the card, initialize the driver. * * Description: * This is the standard attach routine. It initializes the I/O8+ * card, identifies the chip on that card, then allocates and * initializes the various data structures used by the driver * itself. */ int sxattach( device_t dev) { int unit; struct sx_softc *sc; struct tty *tp; struct speedtab *spt; int chip, x, y; char rev; int error; sc = device_get_softc(dev); unit = device_get_unit(dev); sx_flags = device_get_flags(dev); if (sx_numunits < unit + 1) sx_numunits = unit + 1; DPRINT((0, DBG_AUTOBOOT, "sx%d: sxattach\n", unit)); /* Reset the CD1865. */ if ((error = sx_init_cd1865(sc, unit)) != 0) { return(error); } /* * ID the chip: * * Chip revcode pkgtype * GFRCR SRCR bit 7 * CD180 rev B 0x81 0 * CD180 rev C 0x82 0 * CD1864 rev A 0x82 1 * CD1865 rev A 0x83 1 -- Do not use!!! Does not work. * CD1865 rev B 0x84 1 * -- Thanks to Gwen Wang, Cirrus Logic (via Roger Wollf). */ switch (sx_cd1865_in(sc, CD1865_GFRCR)) { case 0x82: chip = 1864; rev = 'A'; break; case 0x83: chip = 1865; rev = 'A'; break; case 0x84: chip = 1865; rev = 'B'; break; case 0x85: chip = 1865; rev = 'C'; break; default: chip = -1; rev = '\0'; break; } if (bootverbose && chip != -1) printf("sx%d: Specialix I/O8+ CD%d processor rev %c\n", unit, chip, rev); DPRINT((0, DBG_AUTOBOOT, "sx%d: GFRCR 0x%02x\n", unit, sx_cd1865_in(sc, CD1865_GFRCR))); #ifdef POLL if (sx_pollrate == 0) { sx_pollrate = POLLHZ; /* in addition to irq */ #ifdef REALPOLL sx_realpoll = 1; /* scan always */ #endif } #endif sc->sc_ports = (struct sx_port *)malloc( sizeof(struct sx_port) * SX_NUMCHANS, M_DEVBUF, M_NOWAIT); if (sc->sc_ports == NULL) { printf("sx%d: No memory for sx_port structs!\n", unit); return(EINVAL); } bzero(sc->sc_ports, sizeof(struct sx_port) * SX_NUMCHANS); /* * Allocate tty structures for the channels. */ tp = (struct tty *)malloc(sizeof(struct tty) * SX_NUMCHANS, M_DEVBUF, M_NOWAIT); if (tp == NULL) { free(sc->sc_ports, M_DEVBUF); printf("sx%d: No memory for tty structs!\n", unit); return(EINVAL); } bzero(tp, sizeof(struct tty) * SX_NUMCHANS); sx__tty = tp; /* * Initialize the channels. */ for (x = 0; x < SX_NUMCHANS; x++) { sc->sc_ports[x].sp_chan = x; sc->sc_ports[x].sp_tty = tp++; sc->sc_ports[x].sp_state = 0; /* internal flag */ sc->sc_ports[x].sp_dtr_wait = 3 * hz; sc->sc_ports[x].sp_iin.c_iflag = TTYDEF_IFLAG; sc->sc_ports[x].sp_iin.c_oflag = TTYDEF_OFLAG; sc->sc_ports[x].sp_iin.c_cflag = TTYDEF_CFLAG; sc->sc_ports[x].sp_iin.c_lflag = TTYDEF_LFLAG; termioschars(&sc->sc_ports[x].sp_iin); sc->sc_ports[x].sp_iin.c_ispeed = TTYDEF_SPEED;; sc->sc_ports[x].sp_iin.c_ospeed = TTYDEF_SPEED;; sc->sc_ports[x].sp_iout = sc->sc_ports[x].sp_iin; } if (done_chartimes == 0) { for (spt = chartimes ; spt->sp_speed != -1; spt++) { if ((spt->sp_code /= hz) == 0) spt->sp_code = 1; } done_chartimes = 1; } /* * Set up the known devices. */ y = unit * (1 << SX_CARDSHIFT); for (x = 0; x < SX_NUMCHANS; x++) { register int num; /* DTR/RTS -> RTS devices. */ num = x + y; make_dev(&sx_cdevsw, x, 0, 0, 0600, "ttyG%02d", x+y); make_dev(&sx_cdevsw, x + 0x00080, 0, 0, 0600, "cuaG%02d", num); make_dev(&sx_cdevsw, x + 0x10000, 0, 0, 0600, "ttyiG%02d", num); make_dev(&sx_cdevsw, x + 0x10080, 0, 0, 0600, "cuaiG%02d", num); make_dev(&sx_cdevsw, x + 0x20000, 0, 0, 0600, "ttylG%02d", num); make_dev(&sx_cdevsw, x + 0x20080, 0, 0, 0600, "cualG%02d", num); /* DTR/RTS -> DTR devices. */ num += SX_NUMCHANS; make_dev(&sx_cdevsw, x + 0x00008, 0, 0, 0600, "ttyG%02d", num); make_dev(&sx_cdevsw, x + 0x00088, 0, 0, 0600, "cuaG%02d", num); make_dev(&sx_cdevsw, x + 0x10008, 0, 0, 0600, "ttyiG%02d", num); make_dev(&sx_cdevsw, x + 0x10088, 0, 0, 0600, "cuaiG%02d", num); make_dev(&sx_cdevsw, x + 0x20008, 0, 0, 0600, "ttylG%02d", num); make_dev(&sx_cdevsw, x + 0x20088, 0, 0, 0600, "cualG%02d", num); } return (0); } /* * sxopen() * Open a port on behalf of a user. * * Description: * This is the standard open routine. */ static int sxopen( dev_t dev, int flag, int mode, d_thread_t *p) { int oldspl, error; int card, chan; struct sx_softc *sc; struct tty *tp; struct sx_port *pp; int mynor = minor(dev); card = SX_MINOR2CARD(mynor); if ((sc = devclass_get_softc(sx_devclass, card)) == NULL) return (ENXIO); chan = SX_MINOR2CHAN(mynor); if (chan >= SX_NUMCHANS) { DPRINT((0, DBG_OPEN|DBG_FAIL, "sx%d: nchans %d\n", card, SX_NUMCHANS)); return(ENXIO); } #ifdef POLL /* * We've now got a device, so start the poller. */ if (init_finished == 0) { timeout(sx_poll, (caddr_t)0L, sx_pollrate); init_finished = 1; } #endif /* initial/lock device */ if (DEV_IS_STATE(mynor)) { return(0); } pp = &(sc->sc_ports[chan]); tp = pp->sp_tty; /* the "real" tty */ dev->si_tty = tp; DPRINT((pp, DBG_ENTRY|DBG_OPEN, "sxopen(%s,%x,%x,%x)\n", devtoname(dev), flag, mode, p)); oldspl = spltty(); /* Keep others out */ error = 0; /* * The minor also indicates whether the DTR pin on this port is wired * as DTR or as RTS. Default is zero, wired as RTS. */ if (DEV_DTRPIN(mynor)) pp->sp_state |= SX_SS_DTRPIN; else pp->sp_state &= ~SX_SS_DTRPIN; pp->sp_state &= SX_SS_XMIT; /* Turn off "transmitting" flag. */ open_top: /* * If DTR is off and we actually do have a DTR pin, sleep waiting for * it to assert. */ while (pp->sp_state & SX_SS_DTR_OFF && SX_DTRPIN(pp)) { error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sxdtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (DEV_IS_CALLOUT(mynor)) { if (!pp->sp_active_out) { error = EBUSY; goto out; } } else { if (pp->sp_active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&pp->sp_active_out, TTIPRI|PCATCH, "sxbi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && suser(p)) { DPRINT((pp, DBG_OPEN|DBG_FAIL, "already open and EXCLUSIVE set\n")); error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Avoid sleep... :-) */ DPRINT((pp, DBG_OPEN, "first open\n")); tp->t_oproc = sx_start; tp->t_stop = sx_stop; tp->t_param = sxparam; tp->t_dev = dev; tp->t_termios = mynor & SX_CALLOUT_MASK ? pp->sp_iout : pp->sp_iin; (void)sx_modem(sc, pp, SET, TIOCM_DTR|TIOCM_RTS); ++pp->sp_wopeners; /* in case of sleep in sxparam */ error = sxparam(tp, &tp->t_termios); --pp->sp_wopeners; if (error != 0) goto out; /* XXX: we should goto_top if sxparam slept */ /* set initial DCD state */ if (DEV_IS_CALLOUT(mynor) || (sx_modem(sc, pp, GET, 0) & TIOCM_CD)) { ttyld_modem(tp, 1); } } /* whoops! we beat the close! */ if (pp->sp_state & SX_SS_CLOSING) { /* try and stop it from proceeding to bash the hardware */ pp->sp_state &= ~SX_SS_CLOSING; } /* * Wait for DCD if necessary */ if (!(tp->t_state & TS_CARR_ON) && !DEV_IS_CALLOUT(mynor) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++pp->sp_wopeners; DPRINT((pp, DBG_OPEN, "sleeping for carrier\n")); error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sxdcd", 0); --pp->sp_wopeners; if (error != 0) goto out; goto open_top; } error = ttyld_open(tp, dev); pp->sp_hotchar = ttyldoptim(tp); if (tp->t_state & TS_ISOPEN && DEV_IS_CALLOUT(mynor)) pp->sp_active_out = TRUE; pp->sp_state |= SX_SS_OPEN; /* made it! */ out: splx(oldspl); DPRINT((pp, DBG_OPEN, "leaving sxopen\n")); if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0) sxhardclose(pp); return(error); } /* * sxclose() * Close a port for a user. * * Description: * This is the standard close routine. */ static int sxclose( dev_t dev, int flag, int mode, d_thread_t *p) { struct sx_port *pp; struct tty *tp; int oldspl; int error = 0; int mynor = minor(dev); if (DEV_IS_SPECIAL(mynor)) return(0); oldspl = spltty(); pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "sxclose(%s,%x,%x,%x) sp_state:%x\n", devtoname(dev), flag, mode, p, pp->sp_state)); /* did we sleep and lose a race? */ if (pp->sp_state & SX_SS_CLOSING) { /* error = ESOMETING? */ goto out; } /* begin race detection.. */ pp->sp_state |= SX_SS_CLOSING; sx_write_enable(pp, 0); /* block writes for ttywait() */ /* THIS MAY SLEEP IN TTYWAIT!!! */ ttyld_close(tp, flag); sx_write_enable(pp, 1); /* did we sleep and somebody started another open? */ if (!(pp->sp_state & SX_SS_CLOSING)) { /* error = ESOMETING? */ goto out; } /* ok. we are now still on the right track.. nuke the hardware */ sxhardclose(pp); ttyclose(tp); pp->sp_state &= ~SX_SS_OPEN; out: DPRINT((pp, DBG_CLOSE|DBG_EXIT, "sxclose out\n")); splx(oldspl); return(error); } /* * sxhardclose() * Do hard-close processing. * * Description: * Called on last close. Handle DTR and RTS, do cleanup. If we have * pending output in the FIFO, wait for it to clear before we shut down * the hardware. */ static void sxhardclose( struct sx_port *pp) { struct sx_softc *sc; struct tty *tp; int oldspl, dcd; oldspl = spltty(); DPRINT((pp, DBG_CLOSE, "sxhardclose sp_state:%x\n", pp->sp_state)); tp = pp->sp_tty; sc = PP2SC(pp); dcd = sx_modem(sc, pp, GET, 0) & TIOCM_CD; if (tp->t_cflag & HUPCL || (!pp->sp_active_out && !dcd && !(pp->sp_iin.c_cflag && CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { disable_intr(); sx_cd1865_out(sc, CD1865_CAR, pp->sp_chan); if (sx_cd1865_in(sc, CD1865_IER|SX_EI) & CD1865_IER_TXRDY) { sx_cd1865_bic(sc, CD1865_IER, CD1865_IER_TXRDY); sx_cd1865_bis(sc, CD1865_IER, CD1865_IER_TXEMPTY); enable_intr(); splx(oldspl); ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH, "sxclose", tp->t_timeout); oldspl = spltty(); } else { enable_intr(); } (void)sx_modem(sc, pp, BIC, TIOCM_DTR|TIOCM_RTS); /* * If we should hold DTR off for a bit and we actually have a * DTR pin to hold down, schedule sxdtrwakeup(). */ if (pp->sp_dtr_wait != 0 && SX_DTRPIN(pp)) { timeout(sxdtrwakeup, pp, pp->sp_dtr_wait); pp->sp_state |= SX_SS_DTR_OFF; } } (void)sx_shutdown_chan(pp); /* Turn off the hardware. */ pp->sp_active_out = FALSE; wakeup((caddr_t)&pp->sp_active_out); wakeup(TSA_CARR_ON(tp)); splx(oldspl); } /* * called at splsoftclock()... */ static void sxdtrwakeup(void *chan) { struct sx_port *pp; int oldspl; oldspl = spltty(); pp = (struct sx_port *)chan; pp->sp_state &= ~SX_SS_DTR_OFF; wakeup(&pp->sp_dtr_wait); splx(oldspl); } /* * sxwrite() * Handle a write to a port on the I/O8+. * * Description: * This just hands processing off to the line discipline. */ static int sxwrite( dev_t dev, struct uio *uio, int flag) { struct sx_softc *sc; struct sx_port *pp; struct tty *tp; int error = 0; int mynor = minor(dev); int oldspl; pp = MINOR2PP(mynor); sc = PP2SC(pp); tp = pp->sp_tty; DPRINT((pp, DBG_WRITE, "sxwrite %s %x %x\n", devtoname(dev), uio, flag)); oldspl = spltty(); /* * If writes are currently blocked, wait on the "real" tty */ while (pp->sp_state & SX_SS_BLOCKWRITE) { pp->sp_state |= SX_SS_WAITWRITE; DPRINT((pp, DBG_WRITE, "sxwrite sleep on SX_SS_BLOCKWRITE\n")); if ((error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH, "sxwrite", tp->t_timeout))) { if (error == EWOULDBLOCK) error = EIO; goto out; } } error = ttyld_write(tp, uio, flag); out: splx(oldspl); DPRINT((pp, DBG_WRITE, "sxwrite out\n")); return (error); } /* * sxioctl() * Handle ioctl() processing. * * Description: * This is the standard serial ioctl() routine. It was cribbed almost * entirely from the si(4) driver. Thanks, Peter. */ static int sxioctl( dev_t dev, u_long cmd, caddr_t data, int flag, d_thread_t *p) { struct sx_softc *sc; struct sx_port *pp; struct tty *tp; int error; int mynor = minor(dev); int oldspl; int blocked = 0; #if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "sxioctl %s %lx %x %x\n", devtoname(dev), cmd, data, flag)); if (DEV_IS_STATE(mynor)) { struct termios *ct; switch (mynor & SX_STATE_MASK) { case SX_INIT_STATE_MASK: ct = DEV_IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin; break; case SX_LOCK_STATE_MASK: ct = DEV_IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin; break; default: return(ENODEV); } switch (cmd) { case TIOCSETA: error = suser(p); if (error != 0) return(error); *ct = *(struct termios *)data; return(0); case TIOCGETA: *(struct termios *)data = *ct; return(0); case TIOCGETD: *(int *)data = TTYDISC; return(0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return(0); default: return(ENOTTY); } } /* * Do the old-style ioctl compat routines... */ #if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return(error); if (cmd != oldcmd) data = (caddr_t)&term; #endif /* * Do the initial / lock state business */ if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & SX_CALLOUT_MASK ? &pp->sp_lout : &pp->sp_lin; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } /* * Block user-level writes to give the ttywait() * a chance to completely drain for commands * that require the port to be in a quiescent state. */ switch (cmd) { case TIOCSETAW: case TIOCSETAF: case TIOCDRAIN: #ifdef COMPAT_43 case TIOCSETP: #endif blocked++; /* block writes for ttywait() and sxparam() */ sx_write_enable(pp, 0); } error = ttyioctl(dev, cmd, data, flag, p); pp->sp_hotchar = ttyldoptim(tp); if (error != ENOTTY) goto out; oldspl = spltty(); sc = PP2SC(pp); /* Need this to do I/O to the card. */ error = 0; switch (cmd) { case TIOCSBRK: /* Send BREAK. */ DPRINT((pp, DBG_IOCTL, "sxioctl %s BRK S\n", devtoname(dev))); /* * If there's already a break state change pending or * we're already sending a break, just ignore this. * Otherwise, just set our flag and start the * transmitter. */ if (!SX_DOBRK(pp) && !SX_BREAK(pp)) { pp->sp_state |= SX_SS_DOBRK; sx_start(tp); } break; case TIOCCBRK: /* Stop sending BREAK. */ DPRINT((pp, DBG_IOCTL, "sxioctl %s BRK E\n", devtoname(dev))); /* * If a break is going, set our flag so we turn it off * when we can, then kick the transmitter. If a break * isn't going and the flag is set, turn it off. */ if (SX_BREAK(pp)) { pp->sp_state |= SX_SS_DOBRK; sx_start(tp); } else { if (SX_DOBRK(pp)) pp->sp_state &= SX_SS_DOBRK; } break; case TIOCSDTR: /* Assert DTR. */ DPRINT((pp, DBG_IOCTL, "sxioctl %s +DTR\n", devtoname(dev))); if (SX_DTRPIN(pp)) /* Using DTR? */ (void)sx_modem(sc, pp, SET, TIOCM_DTR); break; case TIOCCDTR: /* Clear DTR. */ DPRINT((pp, DBG_IOCTL, "sxioctl(%s) -DTR\n", devtoname(dev))); if (SX_DTRPIN(pp)) /* Using DTR? */ (void)sx_modem(sc, pp, SET, 0); break; case TIOCMSET: /* Force all modem signals. */ DPRINT((pp, DBG_IOCTL, "sxioctl %s =%x\n", devtoname(dev), *(int *)data)); (void)sx_modem(sc, pp, SET, *(int *)data); break; case TIOCMBIS: /* Set (some) modem signals. */ DPRINT((pp, DBG_IOCTL, "sxioctl %s +%x\n", devtoname(dev), *(int *)data)); (void)sx_modem(sc, pp, BIS, *(int *)data); break; case TIOCMBIC: /* Clear (some) modem signals. */ DPRINT((pp, DBG_IOCTL, "sxioctl %s -%x\n", devtoname(dev), *(int *)data)); (void)sx_modem(sc, pp, BIC, *(int *)data); break; case TIOCMGET: /* Get state of modem signals. */ *(int *)data = sx_modem(sc, pp, GET, 0); DPRINT((pp, DBG_IOCTL, "sxioctl(%s) got signals 0x%x\n", devtoname(dev), *(int *)data)); break; case TIOCMSDTRWAIT: /* Set "wait on close" delay. */ /* must be root since the wait applies to following logins */ error = suser(p); if (error == 0) pp->sp_dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: /* Get "wait on close" delay. */ *(int *)data = pp->sp_dtr_wait * 100 / hz; break; default: error = ENOTTY; } splx(oldspl); out: DPRINT((pp, DBG_IOCTL|DBG_EXIT, "sxioctl out %d\n", error)); if (blocked) sx_write_enable(pp, 1); return(error); } /* * sxparam() * Configure line parameters. * * Description: * Configure the bitrate, wordsize, flow control and various other serial * port parameters for this line. * * Environment: * Called at spltty(); this may sleep, does not flush nor wait for drain, * nor block writes. Caller must arrange this if it's important.. */ static int sxparam( struct tty *tp, struct termios *t) { struct sx_softc *sc; struct sx_port *pp = TP2PP(tp); int oldspl, cflag, iflag, oflag, lflag; int error = 0; int ispd = 0; int ospd = 0; unsigned char val, cor1, cor2, cor3, ier; sc = PP2SC(pp); DPRINT((pp, DBG_ENTRY|DBG_PARAM, "sxparam %x/%x\n", tp, t)); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; DPRINT((pp, DBG_PARAM, "OF 0x%x CF 0x%x IF 0x%x LF 0x%x\n", oflag, cflag, iflag, lflag)); /* If the port isn't hung up... */ if (t->c_ospeed != 0) { /* Convert bit rate to hardware divisor values. */ ospd = ttspeedtab(t->c_ospeed, bdrates); ispd = t->c_ispeed ? ttspeedtab(t->c_ispeed, bdrates) : ospd; /* We only allow standard bit rates. */ if (ospd < 0 || ispd < 0) return(EINVAL); } oldspl = spltty(); /* Block other activity. */ cor1 = 0; cor2 = 0; cor3 = 0; ier = CD1865_IER_RXD | CD1865_IER_CD; #ifdef notyet /* We don't yet handle this stuff. */ val = 0; if (iflag & IGNBRK) /* Breaks */ val |= BR_IGN; if (iflag & BRKINT) /* Interrupt on break? */ val |= BR_INT; if (iflag & PARMRK) /* Parity mark? */ val |= BR_PARMRK; #endif /* notyet */ /* * If the device isn't hung up, set the serial port bitrates. */ if (t->c_ospeed != 0) { disable_intr(); sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); sx_cd1865_out(sc, CD1865_RBPRH|SX_EI, (ispd >> 8) & 0xff); sx_cd1865_out(sc, CD1865_RBPRL|SX_EI, ispd & 0xff); sx_cd1865_out(sc, CD1865_TBPRH|SX_EI, (ospd >> 8) & 0xff); sx_cd1865_out(sc, CD1865_TBPRL|SX_EI, ospd & 0xff); enable_intr(); } if (cflag & CSTOPB) /* Two stop bits? */ cor1 |= CD1865_COR1_2SB; /* Yep. */ /* * Parity settings. */ val = 0; if (cflag & PARENB) { /* Parity enabled? */ val = CD1865_COR1_NORMPAR; /* Turn on normal parity handling. */ if (cflag & PARODD) /* Odd Parity? */ val |= CD1865_COR1_ODDP; /* Turn it on. */ } else val = CD1865_COR1_NOPAR; /* Turn off parity detection. */ cor1 |= val; if (iflag & IGNPAR) /* Ignore chars with parity errors? */ cor1 |= CD1865_COR1_IGNORE; /* * Set word length. */ if ((cflag & CS8) == CS8) val = CD1865_COR1_8BITS; else if ((cflag & CS7) == CS7) val = CD1865_COR1_7BITS; else if ((cflag & CS6) == CS6) val = CD1865_COR1_6BITS; else val = CD1865_COR1_5BITS; cor1 |= val; /* * Enable hardware RTS/CTS flow control. We can handle output flow * control at any time, since we have a dedicated CTS pin. * Unfortunately, though, the RTS pin is really the DTR pin. This * means that we can't ever use the automatic input flow control of * the CD1865 and that we can only use the pin for input flow * control when it's wired as RTS. */ if (cflag & CCTS_OFLOW) { /* Output flow control... */ pp->sp_state |= SX_SS_OFLOW; #ifdef SX_BROKEN_CTS disable_intr(); sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); if (sx_cd1865_in(sc, CD1865_MSVR|SX_EI) & CD1865_MSVR_CTS) { enable_intr(); pp->sp_state |= SX_SS_OSTOP; sx_write_enable(pp, 0); /* Block writes. */ } else { enable_intr(); } ier |= CD1865_IER_CTS; #else /* SX_BROKEN_CTS */ cor2 |= CD1865_COR2_CTSAE; /* Set CTS automatic enable. */ #endif /* SX_BROKEN_CTS */ } else { pp->sp_state &= ~SX_SS_OFLOW; } if (cflag & CRTS_IFLOW && !SX_DTRPIN(pp)) /* Input flow control. */ pp->sp_state |= SX_SS_IFLOW; else pp->sp_state &= ~SX_SS_IFLOW; if (iflag & IXANY) cor2 |= CD1865_COR2_IXM; /* Any character is XON. */ if (iflag & IXOFF) { cor2 |= CD1865_COR2_TXIBE; /* Enable inband flow control.*/ cor3 |= CD1865_COR3_FCT | CD1865_COR3_SCDE; /* Hide from host */ disable_intr(); sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); /* Sel chan.*/ sx_cd1865_out(sc, CD1865_SCHR1|SX_EI, t->c_cc[VSTART]); sx_cd1865_out(sc, CD1865_SCHR2|SX_EI, t->c_cc[VSTOP]); sx_cd1865_out(sc, CD1865_SCHR3|SX_EI, t->c_cc[VSTART]); sx_cd1865_out(sc, CD1865_SCHR4|SX_EI, t->c_cc[VSTOP]); enable_intr(); } /* * All set, now program the hardware. */ disable_intr(); sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); /* Select channel. */ sx_cd1865_out(sc, CD1865_COR1|SX_EI, cor1); sx_cd1865_out(sc, CD1865_COR2|SX_EI, cor2); sx_cd1865_out(sc, CD1865_COR3|SX_EI, cor3); sx_cd1865_wait_CCR(sc, SX_EI); sx_cd1865_out(sc, CD1865_CCR|SX_EI, CD1865_CCR_CORCHG1|CD1865_CCR_CORCHG2|CD1865_CCR_CORCHG3); sx_cd1865_wait_CCR(sc, SX_EI); enable_intr(); if (SX_DTRPIN(pp)) val = TIOCM_DTR; else val = TIOCM_RTS; if (t->c_ospeed == 0) /* Clear DTR/RTS if we're hung up. */ (void)sx_modem(sc, pp, BIC, val); else /* If we were hung up, we may have to */ (void)sx_modem(sc, pp, BIS, val); /* re-enable the signal. */ /* * Last, enable the receiver and transmitter and turn on the * interrupts we need (receive, carrier-detect and possibly CTS * (iff we're built with SX_BROKEN_CTS and CCTS_OFLOW is on). */ disable_intr(); sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); /* Select channel. */ sx_cd1865_wait_CCR(sc, SX_EI); sx_cd1865_out(sc, CD1865_CCR|SX_EI, CD1865_CCR_RXEN|CD1865_CCR_TXEN); sx_cd1865_wait_CCR(sc, SX_EI); sx_cd1865_out(sc, CD1865_IER|SX_EI, ier); enable_intr(); DPRINT((pp, DBG_PARAM, "sxparam out\n")); splx(oldspl); return(error); } /* * sx_write_enable() * Enable/disable writes to a card channel. * * Description: * Set or clear the SX_SS_BLOCKWRITE flag in sp_state to block or allow * writes to a serial port on the card. When we enable writes, we * wake up anyone sleeping on SX_SS_WAITWRITE for this channel. * * Parameters: * flag 0 - disable writes. * 1 - enable writes. */ static void sx_write_enable( struct sx_port *pp, int flag) { int oldspl; oldspl = spltty(); /* Keep interrupts out. */ if (flag) { /* Enable writes to the channel? */ pp->sp_state &= ~SX_SS_BLOCKWRITE; /* Clear our flag. */ if (pp->sp_state & SX_SS_WAITWRITE) { /* Sleepers? */ pp->sp_state &= ~SX_SS_WAITWRITE; /* Clear their flag */ wakeup((caddr_t)pp); /* & wake them up. */ } } else /* Disabling writes. */ pp->sp_state |= SX_SS_BLOCKWRITE; /* Set our flag. */ splx(oldspl); } /* * sx_shutdown_chan() * Shut down a channel on the I/O8+. * * Description: * This does all hardware shutdown processing for a channel on the I/O8+. * It is called from sxhardclose(). We reset the channel and turn off * interrupts. */ static void sx_shutdown_chan( struct sx_port *pp) { int s; struct sx_softc *sc; DPRINT((pp, DBG_ENTRY, "sx_shutdown_chan %x %x\n", pp, pp->sp_state)); sc = PP2SC(pp); s = spltty(); disable_intr(); sx_cd1865_out(sc, CD1865_CAR, pp->sp_chan); /* Select channel. */ sx_cd1865_wait_CCR(sc, 0); /* Wait for any commands to complete. */ sx_cd1865_out(sc, CD1865_CCR, CD1865_CCR_SOFTRESET); /* Reset chan. */ sx_cd1865_wait_CCR(sc, 0); sx_cd1865_out(sc, CD1865_IER, 0); /* Disable all interrupts. */ enable_intr(); splx(s); } /* * sx_modem() * Set/Get state of modem control lines. * * Description: * Get and set the state of the modem control lines that we have available * on the I/O8+. The only lines we are guaranteed to have are CD and CTS. * We have DTR if the "DTR/RTS pin is DTR" flag is set, otherwise we have * RTS through the DTR pin. */ static int sx_modem( struct sx_softc *sc, struct sx_port *pp, enum sx_mctl cmd, int bits) { int s, x; DPRINT((pp, DBG_ENTRY|DBG_MODEM, "sx_modem %x/%s/%x\n", pp, sx_mctl2str(cmd), bits)); s = spltty(); /* Block interrupts. */ disable_intr(); sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); /* Select our port. */ x = sx_cd1865_in(sc, CD1865_MSVR|SX_EI); /* Get the current signals. */ #ifdef SX_DEBUG DPRINT((pp, DBG_MODEM, "sx_modem MSVR 0x%x, CCSR %x GIVR %x SRSR %x\n", x, sx_cd1865_in(sc, CD1865_CCSR|SX_EI), sx_cd1865_in(sc, CD1865_GIVR|SX_EI), sx_cd1865_in(sc, CD1865_SRSR|SX_EI))); #endif enable_intr(); /* Allow other interrupts. */ switch (cmd) { case GET: bits = TIOCM_LE; if ((x & CD1865_MSVR_CD) == 0) bits |= TIOCM_CD; if ((x & CD1865_MSVR_CTS) == 0) bits |= TIOCM_CTS; if ((x & CD1865_MSVR_DTR) == 0) { if (SX_DTRPIN(pp)) /* Odd pin is DTR? */ bits |= TIOCM_DTR; /* Report DTR. */ else /* Odd pin is RTS. */ bits |= TIOCM_RTS; /* Report RTS. */ } splx(s); return(bits); case SET: x = CD1865_MSVR_OFF; if ((bits & TIOCM_RTS && !SX_DTRPIN(pp)) || (bits & TIOCM_DTR && SX_DTRPIN(pp))) x &= ~CD1865_MSVR_DTR; break; case BIS: if ((bits & TIOCM_RTS && !SX_DTRPIN(pp)) || (bits & TIOCM_DTR && SX_DTRPIN(pp))) x &= ~CD1865_MSVR_DTR; break; case BIC: if ((bits & TIOCM_RTS && !SX_DTRPIN(pp)) || (bits & TIOCM_DTR && SX_DTRPIN(pp))) x |= CD1865_MSVR_DTR; break; } DPRINT((pp, DBG_MODEM, "sx_modem MSVR=0x%x\n", x)); disable_intr(); /* * Set the new modem signals. */ sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); sx_cd1865_out(sc, CD1865_MSVR|SX_EI, x); enable_intr(); splx(s); return 0; } #ifdef POLL /* * sx_poll() * Poller to catch missed interrupts. * * Description: * Only used if we're complied with POLL. This routine is called every * sx_pollrate ticks to check for missed interrupts. We check each card * in the system; if we missed an interrupt, we complain about each one * and later call sx_intr() to handle them. */ static void sx_poll( void *dummy) { struct sx_softc *sc; struct sx_port *pp; int card, lost, oldspl, chan; DPRINT((0, DBG_POLL, "sx_poll\n")); oldspl = spltty(); if (in_interrupt) goto out; lost = 0; for (card = 0; card < sx_numunits; card++) { sc = devclass_get_softc(sx_devclass, card); if (sc == NULL) continue; if (sx_cd1865_in(sc, CD1865_SRSR|SX_EI) & CD1865_SRSR_REQint) { printf("sx%d: lost interrupt\n", card); lost++; } /* * Gripe about no input flow control. */ for (chan = 0; chan < SX_NUMCHANS; pp++, chan++) { pp = &(sc->sc_ports[chan]); if (pp->sp_delta_overflows > 0) { printf("sx%d: %d tty level buffer overflows\n", card, pp->sp_delta_overflows); pp->sp_delta_overflows = 0; } } } if (lost || sx_realpoll) sx_intr(NULL); /* call intr with fake vector */ out: splx(oldspl); timeout(sx_poll, (caddr_t)0L, sx_pollrate); } #endif /* POLL */ /* * sx_transmit() * Handle transmit request interrupt. * * Description: * This routine handles the transmit request interrupt from the CD1865 * chip on the I/O8+ card. The CD1865 interrupts us for a transmit * request under two circumstances: When the last character in the * transmit FIFO is sent and the channel is ready for more characters * ("transmit ready"), or when the last bit of the last character in the * FIFO is actually transmitted ("transmit empty"). In the former case, * we just pass processing off to sx_start() (via the line discipline) * to queue more characters. In the latter case, we were waiting for * the line to flush in sxhardclose() so we need to wake the sleeper. */ static void sx_transmit( struct sx_softc *sc, struct sx_port *pp, int card) { struct tty *tp; unsigned char flags; tp = pp->sp_tty; /* * Let others know what we're doing. */ pp->sp_state |= SX_SS_IXMIT; /* * Get the service request enable register to see what we're waiting * for. */ flags = sx_cd1865_in(sc, CD1865_SRER|SX_EI); DPRINT((pp, DBG_TRANSMIT, "sx_xmit %x SRER %x\n", tp, flags)); /* * "Transmit ready." The transmit FIFO is empty (but there are still * two characters being transmitted), so we need to tell the line * discipline to send more. */ if (flags & CD1865_IER_TXRDY) { ttyld_start(tp); pp->sp_state &= ~SX_SS_IXMIT; DPRINT((pp, DBG_TRANSMIT, "sx_xmit TXRDY out\n")); return; } /* * "Transmit empty." The transmitter is completely empty; turn off the * service request and wake up the guy in sxhardclose() who is waiting * for this. */ if (flags & CD1865_IER_TXEMPTY) { flags &= ~CD1865_IER_TXEMPTY; sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); sx_cd1865_out(sc, CD1865_SRER|SX_EI, flags); wakeup((caddr_t)pp); } pp->sp_state &= ~SX_SS_IXMIT; DPRINT((pp, DBG_TRANSMIT, "sx_xmit out\n")); } /* * sx_modem_state() * Handle modem state-change request interrupt. * * Description: * Handles changed modem signals CD and CTS. We pass the CD change * off to the line discipline. We can't handle DSR since there isn't a * pin for it. */ static void sx_modem_state( struct sx_softc *sc, struct sx_port *pp, int card) { struct tty *tp; unsigned char mcr; /* * Let others know what we're doing. */ pp->sp_state |= SX_SS_IMODEM; tp = pp->sp_tty; /* Grab the Modem Change Register. */ mcr = sx_cd1865_in(sc, CD1865_MCR|SX_EI); DPRINT((pp, DBG_MODEM_STATE, "sx_mdmst %x st %x sp %x mcr %x\n", tp, tp->t_state, pp->sp_state, mcr)); if (mcr & CD1865_MCR_CDCHG) { /* CD changed? */ if ((sx_cd1865_in(sc, CD1865_MSVR) & CD1865_MSVR_CD) == 0) { DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n", tp->t_line)); (void)ttyld_modem(tp, 1); } else { /* CD went down. */ DPRINT((pp, DBG_INTR, "modem carr off\n")); if (ttyld_modem(tp, 0)) (void)sx_modem(sc, pp, SET, 0); } } #ifdef SX_BROKEN_CTS if (mcr & CD1865_MCR_CTSCHG) { /* CTS changed? */ if (sx_cd1865_in(sc, CD1865_MSVR|SX_EI) & CD1865_MSVR_CTS) { pp->sp_state |= SX_SS_OSTOP; sx_cd1865_bic(sc, CD1865_IER|SX_EI, CD1865_IER_TXRDY); sx_write_enable(pp, 0); /* Block writes. */ } else { pp->sp_state &= ~SX_SS_OSTOP; sx_cd1865_bis(sc, CD1865_IER|SX_EI, CD1865_IER_TXRDY); sx_write_enable(pp, 1); /* Unblock writes. */ } } #endif /* SX_BROKEN_CTS */ /* Clear state-change indicator bits. */ sx_cd1865_out(sc, CD1865_MCR|SX_EI, 0); pp->sp_state &= ~SX_SS_IMODEM; } /* * sx_receive() * Handle receive request interrupt. * * Description: * Handle a receive request interrupt from the CD1865. This is just a * standard "we have characters to process" request, we don't have to * worry about exceptions like BREAK and such. Exceptions are handled * by sx_receive_exception(). */ static void sx_receive( struct sx_softc *sc, struct sx_port *pp, int card) { struct tty *tp; unsigned char count; int i, x; static unsigned char sx_rxbuf[SX_BUFFERSIZE]; /* input staging area */ tp = pp->sp_tty; DPRINT((pp, DBG_RECEIVE, "sx_rcv %x st %x sp %x\n", tp, tp->t_state, pp->sp_state)); /* * Let others know what we're doing. */ pp->sp_state |= SX_SS_IRCV; /* * How many characters are waiting for us? */ count = sx_cd1865_in(sc, CD1865_RDCR|SX_EI); if (count == 0) /* None? Bail. */ return; DPRINT((pp, DBG_RECEIVE, "sx_receive count %d\n", count)); /* * Pull the characters off the card into our local buffer, then * process that. */ for (i = 0; i < count; i++) sx_rxbuf[i] = sx_cd1865_in(sc, CD1865_RDR|SX_EI); /* * If we're not open and connected, bail. */ if (!(tp->t_state & TS_CONNECTED && tp->t_state & TS_ISOPEN)) { pp->sp_state &= ~SX_SS_IRCV; DPRINT((pp, DBG_RECEIVE, "sx_rcv not open\n")); return; } /* * If the tty input buffers are blocked and we have an RTS pin, * drop RTS and bail. */ if (tp->t_state & TS_TBLOCK) { if (!SX_DTRPIN(pp) && SX_IFLOW(pp)) { (void)sx_modem(sc, pp, BIC, TIOCM_RTS); pp->sp_state |= SX_SS_ISTOP; } pp->sp_state &= ~SX_SS_IRCV; return; } if (tp->t_state & TS_CAN_BYPASS_L_RINT) { DPRINT((pp, DBG_RECEIVE, "sx_rcv BYPASS\n")); /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or * echo!). slinput is reasonably fast (usually 40 * instructions plus call overhead). */ if (tp->t_rawq.c_cc + count >= SX_I_HIGH_WATER && (tp->t_cflag & CRTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) { ttyblock(tp); DPRINT((pp, DBG_RECEIVE, "sx_rcv block\n")); } tk_nin += count; tk_rawcc += count; tp->t_rawcc += count; pp->sp_delta_overflows += b_to_q((char *)sx_rxbuf, count, &tp->t_rawq); ttwakeup(tp); /* * If we were stopped and need to start again because of this * receive, kick the output routine to get things going again. */ if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; sx_start(tp); } } else { DPRINT((pp, DBG_RECEIVE, "sx_rcv l_rint\n")); /* * It'd be nice to not have to go through the function call * overhead for each char here. It'd be nice to block input * it, saving a loop here and the call/return overhead. */ for (x = 0; x < count; x++) { i = sx_rxbuf[x]; if (ttyld_rint(tp, i) == -1) pp->sp_delta_overflows++; } } pp->sp_state &= ~SX_SS_IRCV; DPRINT((pp, DBG_RECEIVE, "sx_rcv out\n")); } /* * sx_receive_exception() * Handle receive exception request interrupt processing. * * Description: * Handle a receive exception request interrupt from the CD1865. * Possible exceptions include BREAK, overrun, receiver timeout * and parity and frame errors. We don't handle receiver timeout, * we just complain. The rest are passed to ttyinput(). */ static void sx_receive_exception( struct sx_softc *sc, struct sx_port *pp, int card) { struct tty *tp; unsigned char st; int ch, isopen; tp = pp->sp_tty; /* * Let others know what we're doing. */ pp->sp_state |= SX_SS_IRCVEXC; /* * Check to see whether we should receive characters. */ if (tp->t_state & TS_CONNECTED && tp->t_state & TS_ISOPEN) isopen = 1; else isopen = 0; st = sx_cd1865_in(sc, CD1865_RCSR|SX_EI); /* Get the character status.*/ ch = (int)sx_cd1865_in(sc, CD1865_RDR|SX_EI); /* Get the character. */ DPRINT((pp, DBG_RECEIVE_EXC, "sx_rexc %x st %x sp %x st 0x%x ch 0x%x ('%c')\n", tp, tp->t_state, pp->sp_state, st, ch, ch)); /* If there's no status or the tty isn't open, bail. */ if (!st || !isopen) { pp->sp_state &= ~SX_SS_IRCVEXC; DPRINT((pp, DBG_RECEIVE_EXC, "sx_rexc not open\n")); return; } if (st & CD1865_RCSR_TOUT) /* Receiver timeout; just complain. */ printf("sx%d: port %d: Receiver timeout.\n", card, pp->sp_chan); else if (st & CD1865_RCSR_BREAK) ch |= TTY_BI; else if (st & CD1865_RCSR_PE) ch |= TTY_PE; else if (st & CD1865_RCSR_FE) ch |= TTY_FE; else if (st & CD1865_RCSR_OE) ch |= TTY_OE; ttyld_rint(tp, ch); pp->sp_state &= ~SX_SS_IRCVEXC; } /* * sx_intr() * Field interrupts from the I/O8+. * * Description: * The interrupt handler polls ALL ports on ALL adapters each time * it is called. */ void sx_intr( void *arg) { struct sx_softc *sc; struct sx_port *pp = NULL; int card; unsigned char ack; sc = arg; DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "sx_intr\n")); if (in_interrupt) return; in_interrupt = 1; /* * When we get an int we poll all the channels and do ALL pending * work, not just the first one we find. This allows all cards to * share the same vector. * * On the other hand, if we're sharing the vector with something * that's not an I/O8+, we may be making extra work for ourselves. */ for (card = 0; card < sx_numunits; card++) { unsigned char st; sc = devclass_get_softc(sx_devclass, card); if (sc == NULL) continue; /* * Check the Service Request Status Register to see who * interrupted us and why. May be a receive, transmit or * modem-signal-change interrupt. Reading the appropriate * Request Acknowledge Register acknowledges the request and * gives us the contents of the Global Service Vector Register, * which in a daisy-chained configuration (not ours) uniquely * identifies the particular CD1865 and gives us the request * type. We mask off the ID part and use the rest. * * From the CD1865 specs, it appears that only one request can * happen at a time, but in testing it's pretty obvious that * the specs lie. Or perhaps we're just slow enough that the * requests pile up. Regardless, if we try to process more * than one at a time without clearing the previous request * (writing zero to EOIR) first, we hang the card. Thus the * "else if" logic here. */ while ((st = (sx_cd1865_in(sc, CD1865_SRSR|SX_EI)) & CD1865_SRSR_REQint)) { /* * Transmit request interrupt. */ if (st & CD1865_SRSR_TREQint) { ack = sx_cd1865_in(sc, CD1865_TRAR|SX_EI) & CD1865_GIVR_ITMASK; pp = sx_int_port(sc, card); if (pp == NULL) /* Bad channel. */ goto skip; pp->sp_state |= SX_SS_INTR; /* In interrupt. */ if (ack == CD1865_GIVR_IT_TX) sx_transmit(sc, pp, card); else printf("sx%d: Bad transmit ack 0x%02x.\n", card, ack); } /* * Modem signal change request interrupt. */ else if (st & CD1865_SRSR_MREQint) { ack = sx_cd1865_in(sc, CD1865_MRAR|SX_EI) & CD1865_GIVR_ITMASK; pp = sx_int_port(sc, card); if (pp == NULL) /* Bad channel. */ goto skip; pp->sp_state |= SX_SS_INTR; /* In interrupt. */ if (ack == CD1865_GIVR_IT_MODEM) sx_modem_state(sc, pp, card); else printf("sx%d: Bad modem ack 0x%02x.\n", card, ack); } /* * Receive request interrupt. */ else if (st & CD1865_SRSR_RREQint) { ack = sx_cd1865_in(sc, CD1865_RRAR|SX_EI) & CD1865_GIVR_ITMASK; pp = sx_int_port(sc, card); if (pp == NULL) /* Bad channel. */ goto skip; pp->sp_state |= SX_SS_INTR; /* In interrupt. */ if (ack == CD1865_GIVR_IT_RCV) sx_receive(sc, pp, card); else if (ack == CD1865_GIVR_IT_REXC) sx_receive_exception(sc, pp, card); else printf("sx%d: Bad receive ack 0x%02x.\n", card, ack); } /* * None of the above; this is a "can't happen," but * you never know... */ else { printf("sx%d: Bad service request 0x%02x.\n", card, st); } pp->sp_state &= ~SX_SS_INTR; skip: sx_cd1865_out(sc, CD1865_EOIR|SX_EI, 0); /* EOI. */ } /* while (st & CD1865_SRSR_REQint) */ } /* for (card = 0; card < sx_numunits; card++) */ in_interrupt = 0; DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "sx_intr out\n")); } /* * sx_start() * Handle transmit and state-change stuff. * * Description: * This is part of the line discipline processing; at various points in * the line discipline he calls ttstart() which calls the oproc routine, * which is this function. We're called by the line discipline to start * data transmission and to change signal states (for RTS flow control). * We're also called by this driver to perform line-breaks and to actually * do the data transmission. * We can only fill the FIFO from interrupt since the card only makes it * available to us during a service request such as TXRDY; this only * happens at interrupt. * * All paths through this code call ttwwakeup(). */ static void sx_start( struct tty *tp) { struct sx_softc *sc; struct sx_port *pp; struct clist *qp; int s; int count = CD1865_TFIFOSZ; s = spltty(); pp = TP2PP(tp); qp = &tp->t_outq; DPRINT((pp, DBG_ENTRY|DBG_START, "sx_start %x st %x sp %x cc %d\n", tp, tp->t_state, pp->sp_state, qp->c_cc)); /* * If we're stopped, just wake up sleepers and get out. */ if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) { ttwwakeup(tp); splx(s); DPRINT((pp, DBG_EXIT|DBG_START, "sx_start out\n", tp->t_state)); return; } sc = TP2SC(tp); /* * If we're not transmitting, we may have been called to crank up the * transmitter and start things rolling or we may have been called to * get a bit of tty state. If the latter, handle it. Either way, if * we have data to transmit, turn on the transmit-ready interrupt, * set the XMIT flag and we're done. As soon as we allow interrupts * the card will interrupt for the first chunk of data. Note that * we don't mark the tty as busy until we are actually sending data * and then only if we have more than will fill the FIFO. If there's * no data to transmit, just handle the tty state. */ if (!SX_XMITTING(pp)) { /* * If we were flow-controlled and input is no longer blocked, * raise RTS if we can. */ if (SX_ISTOP(pp) && !(tp->t_state & TS_TBLOCK)) { if (!SX_DTRPIN(pp) && SX_IFLOW(pp)) (void)sx_modem(sc, pp, BIS, TIOCM_RTS); pp->sp_state &= ~SX_SS_ISTOP; } /* * If input is blocked, drop RTS if we can and set our flag. */ if (tp->t_state & TS_TBLOCK) { if (!SX_DTRPIN(pp) && SX_IFLOW(pp)) (void)sx_modem(sc, pp, BIC, TIOCM_RTS); pp->sp_state |= SX_SS_ISTOP; } if ((qp->c_cc > 0 && !SX_OSTOP(pp)) || SX_DOBRK(pp)) { disable_intr(); sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); sx_cd1865_bis(sc, CD1865_IER|SX_EI, CD1865_IER_TXRDY); enable_intr(); pp->sp_state |= SX_SS_XMIT; } ttwwakeup(tp); splx(s); DPRINT((pp, DBG_EXIT|DBG_START, "sx_start out B st %x sp %x cc %d\n", tp->t_state, pp->sp_state, qp->c_cc)); return; } /* * If we weren't called from an interrupt or it wasn't a transmit * interrupt, we've done all we need to do. Everything else is done * in the transmit interrupt. */ if (!SX_INTR(pp) || !SX_IXMIT(pp)) { ttwwakeup(tp); splx(s); DPRINT((pp, DBG_EXIT|DBG_START, "sx_start out X\n")); return; } /* * We're transmitting. If the clist is empty and we don't have a break * to send, turn off transmit-ready interrupts, and clear the XMIT * flag. Mark the tty as no longer busy, in case we haven't done * that yet. A future call to sxwrite() with more characters will * start up the process once more. */ if (qp->c_cc == 0 && !SX_DOBRK(pp)) { disable_intr(); /* sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan);*/ sx_cd1865_bic(sc, CD1865_IER|SX_EI, CD1865_IER_TXRDY); enable_intr(); pp->sp_state &= ~SX_SS_XMIT; tp->t_state &= ~TS_BUSY; ttwwakeup(tp); splx(s); DPRINT((pp, DBG_EXIT|DBG_START, "sx_start out E st %x sp %x\n", tp->t_state, pp->sp_state)); return; } disable_intr(); /* * If we have a BREAK state-change pending, handle it. If we aren't * sending a break, start one. If we are, turn it off. */ if (SX_DOBRK(pp)) { count -= 2; /* Account for escape chars in FIFO. */ if (SX_BREAK(pp)) { /* Doing break, stop it. */ sx_cd1865_out(sc, CD1865_TDR, CD1865_C_ESC); sx_cd1865_out(sc, CD1865_TDR, CD1865_C_EBRK); sx_cd1865_etcmode(sc, SX_EI, pp->sp_chan, 0); pp->sp_state &= ~SX_SS_BREAK; } else { /* Start doing break. */ sx_cd1865_etcmode(sc, SX_EI, pp->sp_chan, 1); sx_cd1865_out(sc, CD1865_TDR, CD1865_C_ESC); sx_cd1865_out(sc, CD1865_TDR, CD1865_C_SBRK); pp->sp_state |= SX_SS_BREAK; } pp->sp_state &= ~SX_SS_DOBRK; } /* * We've still got data in the clist, fill the channel's FIFO. The * CD1865 only gives us access to the FIFO during a transmit ready * request [interrupt] for this channel. */ while (qp->c_cc > 0 && count-- >= 0) { register unsigned char ch, *cp; int nch; ch = (char)getc(qp); /* * If we're doing a break we're in ETC mode, so we need to * double any NULs in the stream. */ if (SX_BREAK(pp)) { /* Doing break, in ETC mode. */ if (ch == '\0') { /* NUL? Double it. */ sx_cd1865_out(sc, CD1865_TDR, ch); count--; } /* * Peek the next character; if it's a NUL, we need * to escape it, but we can't if we're out of FIFO. * We'll do it on the next pass and leave the FIFO * incompletely filled. */ if (qp->c_cc > 0) { cp = qp->c_cf; cp = nextc(qp, cp, &nch); if (nch == '\0' && count < 1) count = -1; } } sx_cd1865_out(sc, CD1865_TDR, ch); } enable_intr(); /* * If we still have data to transmit, mark the tty busy for the * line discipline. */ if (qp->c_cc > 0) tp->t_state |= TS_BUSY; else tp->t_state &= ~TS_BUSY; /* Wake up sleepers if necessary. */ ttwwakeup(tp); splx(s); DPRINT((pp, DBG_EXIT|DBG_START, "sx_start out R %d/%d\n", count, qp->c_cc)); } /* * Stop output on a line. called at spltty(); */ void sx_stop( struct tty *tp, int rw) { struct sx_softc *sc; struct sx_port *pp; int s; sc = TP2SC(tp); pp = TP2PP(tp); DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sx_stop(%x,%x)\n", tp, rw)); s = spltty(); /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ if (rw & FWRITE) { disable_intr(); sx_cd1865_out(sc, CD1865_CAR|SX_EI, pp->sp_chan); sx_cd1865_bic(sc, CD1865_IER|SX_EI, CD1865_IER_TXRDY); sx_cd1865_wait_CCR(sc, SX_EI); /* Wait for CCR to go idle. */ sx_cd1865_out(sc, CD1865_CCR|SX_EI, CD1865_CCR_TXDIS); sx_cd1865_wait_CCR(sc, SX_EI); enable_intr(); /* what level are we meant to be flushing anyway? */ if (tp->t_state & TS_BUSY) { if ((tp->t_state & TS_TTSTOP) == 0) tp->t_state |= TS_FLUSH; tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } } /* * Nothing to do for FREAD. */ splx(s); } #ifdef SX_DEBUG void sx_dprintf( struct sx_port *pp, int flags, const char *fmt, ...) { static char *logbuf = NULL; static char *linebuf = NULL; static char *logptr; char *lbuf; int n, m; va_list ap; if (logbuf == NULL) { logbuf = (char *)malloc(1024*1024, M_DEVBUF, M_WAITOK); linebuf = (char *)malloc(256, M_DEVBUF, M_WAITOK); logptr = logbuf; } lbuf = linebuf; n = 0; if ((pp == NULL && (sx_debug&flags)) || (pp != NULL && ((pp->sp_debug&flags) || (sx_debug&flags)))) { if (pp != NULL && pp->sp_tty != NULL && pp->sp_tty->t_dev != NULL) { n = snprintf(linebuf, 256, "%cx%d(%d): ", 's', (int)SX_MINOR2CARD(minor(pp->sp_tty->t_dev)), (int)SX_MINOR2CHAN(minor(pp->sp_tty->t_dev))); if (n > 256) n = 256; lbuf += n; } m = n; va_start(ap, fmt); n = vsnprintf(lbuf, 256 - m, fmt, ap); va_end(ap); if (n > 256 - m) n = 256 - m; n += m; if (logptr + n + 1 > logbuf + (1024 * 1024)) { bzero(logptr, logbuf + (1024 * 1024) - logptr); logptr = logbuf; } bcopy(linebuf, logptr, n); logptr += n; *logptr = '\0'; if (sx_debug & DBG_PRINTF) printf("%s", linebuf); } } static char * sx_mctl2str(enum sx_mctl cmd) { switch (cmd) { case GET: return("GET"); case SET: return("SET"); case BIS: return("BIS"); case BIC: return("BIC"); } return("BAD"); } #endif /* DEBUG */ Index: head/sys/dev/usb/ucom.c =================================================================== --- head/sys/dev/usb/ucom.c (revision 130343) +++ head/sys/dev/usb/ucom.c (revision 130344) @@ -1,1169 +1,1169 @@ /* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ /*- * Copyright (c) 2001-2002, Shunsuke Akiyama . * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); /* * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * TODO: * 1. How do I handle hotchar? */ #include #include #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #ifdef USB_DEBUG static int ucomdebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, &ucomdebug, 0, "ucom debug level"); #define DPRINTF(x) do { \ if (ucomdebug) \ logprintf x; \ } while (0) #define DPRINTFN(n, x) do { \ if (ucomdebug > (n)) \ logprintf x; \ } while (0) #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif Static d_open_t ucomopen; Static d_close_t ucomclose; Static d_read_t ucomread; Static d_write_t ucomwrite; Static d_ioctl_t ucomioctl; static struct cdevsw ucom_cdevsw = { .d_version = D_VERSION, .d_open = ucomopen, .d_close = ucomclose, .d_read = ucomread, .d_write = ucomwrite, .d_ioctl = ucomioctl, .d_name = "ucom", .d_flags = D_TTY | D_NEEDGIANT, #if __FreeBSD_version < 500014 .d_bmaj = -1, #endif }; Static void ucom_cleanup(struct ucom_softc *); Static int ucomctl(struct ucom_softc *, int, int); Static int ucomparam(struct tty *, struct termios *); Static void ucomstart(struct tty *); Static void ucomstop(struct tty *, int); Static void ucom_shutdown(struct ucom_softc *); Static void ucom_dtr(struct ucom_softc *, int); Static void ucom_rts(struct ucom_softc *, int); Static void ucom_break(struct ucom_softc *, int); Static usbd_status ucomstartread(struct ucom_softc *); Static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status); Static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status); Static void ucomstopread(struct ucom_softc *); devclass_t ucom_devclass; static moduledata_t ucom_mod = { "ucom", NULL, NULL }; DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_DEPEND(ucom, usb, 1, 1, 1); MODULE_VERSION(ucom, UCOM_MODVER); int ucom_attach(struct ucom_softc *sc) { struct tty *tp; int unit; unit = device_get_unit(sc->sc_dev); sc->sc_tty = tp = ttymalloc(sc->sc_tty); tp->t_oproc = ucomstart; tp->t_param = ucomparam; tp->t_stop = ucomstop; DPRINTF(("ucom_attach: tty_attach tp = %p\n", tp)); DPRINTF(("ucom_attach: make_dev: ucom%d\n", unit)); sc->dev = make_dev(&ucom_cdevsw, unit | UCOM_CALLOUT_MASK, UID_UUCP, GID_DIALER, 0660, "ucom%d", unit); sc->dev->si_tty = tp; return (0); } int ucom_detach(struct ucom_softc *sc) { struct tty *tp = sc->sc_tty; int s; DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty)); sc->sc_dying = 1; if (sc->sc_bulkin_pipe != NULL) usbd_abort_pipe(sc->sc_bulkin_pipe); if (sc->sc_bulkout_pipe != NULL) usbd_abort_pipe(sc->sc_bulkout_pipe); if (tp != NULL) { if (tp->t_state & TS_ISOPEN) { device_printf(sc->sc_dev, "still open, forcing close\n"); ttyld_close(tp, 0); ttyclose(tp); } } else { DPRINTF(("ucom_detach: no tty\n")); return (0); } s = splusb(); if (--sc->sc_refcnt >= 0) { /* Wait for processes to go away. */ usb_detach_wait(USBDEV(sc->sc_dev)); } splx(s); destroy_dev(sc->dev); return (0); } Static void ucom_shutdown(struct ucom_softc *sc) { struct tty *tp = sc->sc_tty; DPRINTF(("ucom_shutdown\n")); /* * Hang up if necessary. Wait a bit, so the other side has time to * notice even if we immediately open the port again. */ if (ISSET(tp->t_cflag, HUPCL)) { (void)ucomctl(sc, TIOCM_DTR, DMBIC); (void)tsleep(sc, TTIPRI, "ucomsd", hz); } } Static int ucomopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { int unit = UCOMUNIT(dev); struct ucom_softc *sc; usbd_status err; struct tty *tp; int s; int error; USB_GET_SC_OPEN(ucom, unit, sc); if (sc->sc_dying) return (ENXIO); tp = sc->sc_tty; DPRINTF(("%s: ucomopen: tp = %p\n", USBDEVNAME(sc->sc_dev), tp)); if (ISSET(tp->t_state, TS_ISOPEN) && ISSET(tp->t_state, TS_XCLUDE) && suser(p)) return (EBUSY); /* * Do the following iff this is a first open. */ s = spltty(); while (sc->sc_opening) tsleep(&sc->sc_opening, PRIBIO, "ucomop", 0); sc->sc_opening = 1; if (!ISSET(tp->t_state, TS_ISOPEN)) { struct termios t; sc->sc_poll = 0; sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0; tp->t_dev = dev; /* * Initialize the termios status to the defaults. Add in the * sticky bits from TIOCSFLAGS. */ t.c_ispeed = 0; t.c_ospeed = TTYDEF_SPEED; t.c_cflag = TTYDEF_CFLAG; /* Make sure ucomparam() will do something. */ tp->t_ospeed = 0; (void)ucomparam(tp, &t); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; ttychars(tp); ttsetwater(tp); /* * Turn on DTR. We must always do this, even if carrier is not * present, because otherwise we'd have to use TIOCSDTR * immediately after setting CLOCAL, which applications do not * expect. We always assert DTR while the device is open * unless explicitly requested to deassert it. */ (void)ucomctl(sc, TIOCM_DTR | TIOCM_RTS, DMBIS); /* Device specific open */ if (sc->sc_callback->ucom_open != NULL) { error = sc->sc_callback->ucom_open(sc->sc_parent, sc->sc_portno); if (error) { ucom_cleanup(sc); sc->sc_opening = 0; wakeup(&sc->sc_opening); splx(s); return (error); } } DPRINTF(("ucomopen: open pipes in = %d out = %d\n", sc->sc_bulkin_no, sc->sc_bulkout_no)); /* Open the bulk pipes */ /* Bulk-in pipe */ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0, &sc->sc_bulkin_pipe); if (err) { printf("%s: open bulk in error (addr %d): %s\n", USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no, usbd_errstr(err)); error = EIO; goto fail_0; } /* Bulk-out pipe */ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no, USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); if (err) { printf("%s: open bulk out error (addr %d): %s\n", USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no, usbd_errstr(err)); error = EIO; goto fail_1; } /* Allocate a request and an input buffer and start reading. */ sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev); if (sc->sc_ixfer == NULL) { error = ENOMEM; goto fail_2; } sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer, sc->sc_ibufsizepad); if (sc->sc_ibuf == NULL) { error = ENOMEM; goto fail_3; } sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev); if (sc->sc_oxfer == NULL) { error = ENOMEM; goto fail_3; } sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer, sc->sc_obufsize + sc->sc_opkthdrlen); if (sc->sc_obuf == NULL) { error = ENOMEM; goto fail_4; } /* * Handle initial DCD. */ if (ISSET(sc->sc_msr, UMSR_DCD) || (minor(dev) & UCOM_CALLOUT_MASK)) ttyld_modem(tp, 1); ucomstartread(sc); } sc->sc_opening = 0; wakeup(&sc->sc_opening); splx(s); error = ttyopen(dev, tp); if (error) goto bad; error = ttyld_open(tp, dev); if (error) goto bad; sc->hotchar = ttyldoptim(tp); DPRINTF(("%s: ucomopen: success\n", USBDEVNAME(sc->sc_dev))); sc->sc_poll = 1; sc->sc_refcnt++; return (0); fail_4: usbd_free_xfer(sc->sc_oxfer); sc->sc_oxfer = NULL; fail_3: usbd_free_xfer(sc->sc_ixfer); sc->sc_ixfer = NULL; fail_2: usbd_close_pipe(sc->sc_bulkout_pipe); sc->sc_bulkout_pipe = NULL; fail_1: usbd_close_pipe(sc->sc_bulkin_pipe); sc->sc_bulkin_pipe = NULL; fail_0: sc->sc_opening = 0; wakeup(&sc->sc_opening); splx(s); return (error); bad: if (!ISSET(tp->t_state, TS_ISOPEN)) { /* * We failed to open the device, and nobody else had it opened. * Clean up the state as appropriate. */ ucom_cleanup(sc); } DPRINTF(("%s: ucomopen: failed\n", USBDEVNAME(sc->sc_dev))); return (error); } static int ucomclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct ucom_softc *sc; struct tty *tp; int s; USB_GET_SC(ucom, UCOMUNIT(dev), sc); tp = sc->sc_tty; DPRINTF(("%s: ucomclose: unit = %d\n", USBDEVNAME(sc->sc_dev), UCOMUNIT(dev))); if (!ISSET(tp->t_state, TS_ISOPEN)) goto quit; s = spltty(); ttyld_close(tp, flag); sc->hotchar = ttyldoptim(tp); ttyclose(tp); splx(s); if (sc->sc_dying) goto quit; if (!ISSET(tp->t_state, TS_ISOPEN)) { /* * Although we got a last close, the device may still be in * use; e.g. if this was the dialout node, and there are still * processes waiting for carrier on the non-dialout node. */ ucom_cleanup(sc); } if (sc->sc_callback->ucom_close != NULL) sc->sc_callback->ucom_close(sc->sc_parent, sc->sc_portno); quit: if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (0); } static int ucomread(dev_t dev, struct uio *uio, int flag) { struct ucom_softc *sc; struct tty *tp; int error; USB_GET_SC(ucom, UCOMUNIT(dev), sc); tp = sc->sc_tty; DPRINTF(("ucomread: tp = %p, flag = 0x%x\n", tp, flag)); if (sc->sc_dying) return (EIO); error = ttyld_read(tp, uio, flag); DPRINTF(("ucomread: error = %d\n", error)); return (error); } static int ucomwrite(dev_t dev, struct uio *uio, int flag) { struct ucom_softc *sc; struct tty *tp; int error; USB_GET_SC(ucom, UCOMUNIT(dev), sc); tp = sc->sc_tty; DPRINTF(("ucomwrite: tp = %p, flag = 0x%x\n", tp, flag)); if (sc->sc_dying) return (EIO); error = ttyld_write(tp, uio, flag); DPRINTF(("ucomwrite: error = %d\n", error)); return (error); } static int ucomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { struct ucom_softc *sc; struct tty *tp; int error; int s; int d; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif USB_GET_SC(ucom, UCOMUNIT(dev), sc); tp = sc->sc_tty; if (sc->sc_dying) return (EIO); DPRINTF(("ucomioctl: cmd = 0x%08lx\n", cmd)); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif error = ttyioctl(dev, cmd, data, flag, p); sc->hotchar = ttyldoptim(tp); if (error != ENOTTY) { DPRINTF(("ucomioctl: l_ioctl: error = %d\n", error)); return (error); } s = spltty(); if (sc->sc_callback->ucom_ioctl != NULL) { error = sc->sc_callback->ucom_ioctl(sc->sc_parent, sc->sc_portno, cmd, data, flag, p); if (error >= 0) return (error); } error = 0; DPRINTF(("ucomioctl: our cmd = 0x%08lx\n", cmd)); switch (cmd) { case TIOCSBRK: DPRINTF(("ucomioctl: TIOCSBRK\n")); ucom_break(sc, 1); break; case TIOCCBRK: DPRINTF(("ucomioctl: TIOCCBRK\n")); ucom_break(sc, 0); break; case TIOCSDTR: DPRINTF(("ucomioctl: TIOCSDTR\n")); (void)ucomctl(sc, TIOCM_DTR, DMBIS); break; case TIOCCDTR: DPRINTF(("ucomioctl: TIOCCDTR\n")); (void)ucomctl(sc, TIOCM_DTR, DMBIC); break; case TIOCMSET: d = *(int *)data; DPRINTF(("ucomioctl: TIOCMSET, 0x%x\n", d)); (void)ucomctl(sc, d, DMSET); break; case TIOCMBIS: d = *(int *)data; DPRINTF(("ucomioctl: TIOCMBIS, 0x%x\n", d)); (void)ucomctl(sc, d, DMBIS); break; case TIOCMBIC: d = *(int *)data; DPRINTF(("ucomioctl: TIOCMBIC, 0x%x\n", d)); (void)ucomctl(sc, d, DMBIC); break; case TIOCMGET: d = ucomctl(sc, 0, DMGET); DPRINTF(("ucomioctl: TIOCMGET, 0x%x\n", d)); *(int *)data = d; break; default: DPRINTF(("ucomioctl: error: our cmd = 0x%08lx\n", cmd)); error = ENOTTY; break; } splx(s); return (error); } Static int ucomctl(struct ucom_softc *sc, int bits, int how) { int mcr; int msr; int onoff; DPRINTF(("ucomctl: bits = 0x%x, how = %d\n", bits, how)); if (how == DMGET) { SET(bits, TIOCM_LE); /* always set TIOCM_LE bit */ DPRINTF(("ucomctl: DMGET: LE")); mcr = sc->sc_mcr; if (ISSET(mcr, UMCR_DTR)) { SET(bits, TIOCM_DTR); DPRINTF((" DTR")); } if (ISSET(mcr, UMCR_RTS)) { SET(bits, TIOCM_RTS); DPRINTF((" RTS")); } msr = sc->sc_msr; if (ISSET(msr, UMSR_CTS)) { SET(bits, TIOCM_CTS); DPRINTF((" CTS")); } if (ISSET(msr, UMSR_DCD)) { SET(bits, TIOCM_CD); DPRINTF((" CD")); } if (ISSET(msr, UMSR_DSR)) { SET(bits, TIOCM_DSR); DPRINTF((" DSR")); } if (ISSET(msr, UMSR_RI)) { SET(bits, TIOCM_RI); DPRINTF((" RI")); } DPRINTF(("\n")); return (bits); } mcr = 0; if (ISSET(bits, TIOCM_DTR)) SET(mcr, UMCR_DTR); if (ISSET(bits, TIOCM_RTS)) SET(mcr, UMCR_RTS); switch (how) { case DMSET: sc->sc_mcr = mcr; break; case DMBIS: sc->sc_mcr |= mcr; break; case DMBIC: sc->sc_mcr &= ~mcr; break; } onoff = ISSET(sc->sc_mcr, UMCR_DTR) ? 1 : 0; ucom_dtr(sc, onoff); onoff = ISSET(sc->sc_mcr, UMCR_RTS) ? 1 : 0; ucom_rts(sc, onoff); return (0); } Static void ucom_break(struct ucom_softc *sc, int onoff) { DPRINTF(("ucom_break: onoff = %d\n", onoff)); if (sc->sc_callback->ucom_set == NULL) return; sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, UCOM_SET_BREAK, onoff); } Static void ucom_dtr(struct ucom_softc *sc, int onoff) { DPRINTF(("ucom_dtr: onoff = %d\n", onoff)); if (sc->sc_callback->ucom_set == NULL) return; sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, UCOM_SET_DTR, onoff); } Static void ucom_rts(struct ucom_softc *sc, int onoff) { DPRINTF(("ucom_rts: onoff = %d\n", onoff)); if (sc->sc_callback->ucom_set == NULL) return; sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, UCOM_SET_RTS, onoff); } void ucom_status_change(struct ucom_softc *sc) { struct tty *tp = sc->sc_tty; u_char old_msr; int onoff; if (sc->sc_callback->ucom_get_status == NULL) { sc->sc_lsr = 0; sc->sc_msr = 0; return; } old_msr = sc->sc_msr; sc->sc_callback->ucom_get_status(sc->sc_parent, sc->sc_portno, &sc->sc_lsr, &sc->sc_msr); if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD)) { if (sc->sc_poll == 0) return; onoff = ISSET(sc->sc_msr, UMSR_DCD) ? 1 : 0; DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff)); ttyld_modem(tp, onoff); } } Static int ucomparam(struct tty *tp, struct termios *t) { struct ucom_softc *sc; int error; usbd_status uerr; USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc); if (sc->sc_dying) return (EIO); DPRINTF(("ucomparam: sc = %p\n", sc)); /* Check requested parameters. */ if (t->c_ospeed < 0) { DPRINTF(("ucomparam: negative ospeed\n")); return (EINVAL); } if (t->c_ispeed && t->c_ispeed != t->c_ospeed) { DPRINTF(("ucomparam: mismatch ispeed and ospeed\n")); return (EINVAL); } /* * If there were no changes, don't do anything. This avoids dropping * input and improves performance when all we did was frob things like * VMIN and VTIME. */ if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag) return (0); /* And copy to tty. */ tp->t_ispeed = 0; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; if (sc->sc_callback->ucom_param == NULL) return (0); ucomstopread(sc); error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t); if (error) { DPRINTF(("ucomparam: callback: error = %d\n", error)); return (error); } ttsetwater(tp); if (t->c_cflag & CRTS_IFLOW) { sc->sc_state |= UCS_RTS_IFLOW; } else if (sc->sc_state & UCS_RTS_IFLOW) { sc->sc_state &= ~UCS_RTS_IFLOW; (void)ucomctl(sc, UMCR_RTS, DMBIS); } sc->hotchar = ttyldoptim(tp); uerr = ucomstartread(sc); if (uerr != USBD_NORMAL_COMPLETION) return (EIO); return (0); } Static void ucomstart(struct tty *tp) { struct ucom_softc *sc; struct cblock *cbp; usbd_status err; int s; u_char *data; int cnt; USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc); DPRINTF(("ucomstart: sc = %p\n", sc)); if (sc->sc_dying) return; s = spltty(); if (tp->t_state & TS_TBLOCK) { if (ISSET(sc->sc_mcr, UMCR_RTS) && ISSET(sc->sc_state, UCS_RTS_IFLOW)) { DPRINTF(("ucomstart: clear RTS\n")); (void)ucomctl(sc, UMCR_RTS, DMBIC); } } else { if (!ISSET(sc->sc_mcr, UMCR_RTS) && tp->t_rawq.c_cc <= tp->t_ilowat && ISSET(sc->sc_state, UCS_RTS_IFLOW)) { DPRINTF(("ucomstart: set RTS\n")); (void)ucomctl(sc, UMCR_RTS, DMBIS); } } if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); DPRINTF(("ucomstart: stopped\n")); goto out; } if (tp->t_outq.c_cc <= tp->t_olowat) { if (ISSET(tp->t_state, TS_SO_OLOWAT)) { CLR(tp->t_state, TS_SO_OLOWAT); wakeup(TSA_OLOWAT(tp)); } selwakeuppri(&tp->t_wsel, TTIPRI); if (tp->t_outq.c_cc == 0) { if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) == TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) { CLR(tp->t_state, TS_SO_OCOMPLETE); wakeup(TSA_OCOMPLETE(tp)); } goto out; } } /* Grab the first contiguous region of buffer space. */ data = tp->t_outq.c_cf; cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND); cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc); if (cnt == 0) { DPRINTF(("ucomstart: cnt == 0\n")); goto out; } SET(tp->t_state, TS_BUSY); if (cnt > sc->sc_obufsize) { DPRINTF(("ucomstart: big buffer %d chars\n", cnt)); cnt = sc->sc_obufsize; } if (sc->sc_callback->ucom_write != NULL) sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno, sc->sc_obuf, data, &cnt); else memcpy(sc->sc_obuf, data, cnt); DPRINTF(("ucomstart: %d chars\n", cnt)); usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, (usbd_private_handle)sc, sc->sc_obuf, cnt, USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb); /* What can we do on error? */ err = usbd_transfer(sc->sc_oxfer); if (err != USBD_IN_PROGRESS) printf("ucomstart: err=%s\n", usbd_errstr(err)); ttwwakeup(tp); out: splx(s); } Static void ucomstop(struct tty *tp, int flag) { struct ucom_softc *sc; int s; USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc); DPRINTF(("ucomstop: %d\n", flag)); if (flag & FREAD) { DPRINTF(("ucomstop: read\n")); ucomstopread(sc); } if (flag & FWRITE) { DPRINTF(("ucomstop: write\n")); s = spltty(); if (ISSET(tp->t_state, TS_BUSY)) { /* XXX do what? */ if (!ISSET(tp->t_state, TS_TTSTOP)) SET(tp->t_state, TS_FLUSH); } splx(s); } DPRINTF(("ucomstop: done\n")); } Static void ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) { struct ucom_softc *sc = (struct ucom_softc *)p; struct tty *tp = sc->sc_tty; u_int32_t cc; int s; DPRINTF(("ucomwritecb: status = %d\n", status)); if (status == USBD_CANCELLED || sc->sc_dying) goto error; if (status != USBD_NORMAL_COMPLETION) { printf("%s: ucomwritecb: %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); /* XXX we should restart after some delay. */ goto error; } usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL); DPRINTF(("ucomwritecb: cc = %d\n", cc)); if (cc <= sc->sc_opkthdrlen) { printf("%s: sent size too small, cc = %d\n", USBDEVNAME(sc->sc_dev), cc); goto error; } /* convert from USB bytes to tty bytes */ cc -= sc->sc_opkthdrlen; s = spltty(); CLR(tp->t_state, TS_BUSY); if (ISSET(tp->t_state, TS_FLUSH)) CLR(tp->t_state, TS_FLUSH); else ndflush(&tp->t_outq, cc); ttyld_start(tp); splx(s); return; error: s = spltty(); CLR(tp->t_state, TS_BUSY); splx(s); return; } Static usbd_status ucomstartread(struct ucom_softc *sc) { usbd_status err; DPRINTF(("ucomstartread: start\n")); sc->sc_state &= ~UCS_RXSTOP; if (sc->sc_bulkin_pipe == NULL) return (USBD_NORMAL_COMPLETION); usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe, (usbd_private_handle)sc, sc->sc_ibuf, sc->sc_ibufsize, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, ucomreadcb); err = usbd_transfer(sc->sc_ixfer); if (err != USBD_IN_PROGRESS) { DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err))); return (err); } return (USBD_NORMAL_COMPLETION); } Static void ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) { struct ucom_softc *sc = (struct ucom_softc *)p; struct tty *tp = sc->sc_tty; usbd_status err; u_int32_t cc; u_char *cp; int lostcc; int s; DPRINTF(("ucomreadcb: status = %d\n", status)); if (status != USBD_NORMAL_COMPLETION) { if (!(sc->sc_state & UCS_RXSTOP)) printf("%s: ucomreadcb: %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); /* XXX we should restart after some delay. */ return; } usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL); DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp)); if (cc == 0) goto resubmit; if (sc->sc_callback->ucom_read != NULL) sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno, &cp, &cc); if (cc > sc->sc_ibufsize) { printf("%s: invalid receive data size, %d chars\n", USBDEVNAME(sc->sc_dev), cc); goto resubmit; } if (cc < 1) goto resubmit; s = spltty(); if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + cc > tp->t_ihiwat && (sc->sc_state & UCS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); lostcc = b_to_q((char *)cp, cc, &tp->t_rawq); tp->t_rawcc += cc; ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; ucomstart(tp); } if (lostcc > 0) printf("%s: lost %d chars\n", USBDEVNAME(sc->sc_dev), lostcc); } else { /* Give characters to tty layer. */ while (cc > 0) { DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp)); if (ttyld_rint(tp, *cp) == -1) { /* XXX what should we do? */ printf("%s: lost %d chars\n", USBDEVNAME(sc->sc_dev), cc); break; } cc--; cp++; } } splx(s); resubmit: err = ucomstartread(sc); if (err) { printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev)); /* XXX what should we dow now? */ } if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, UMCR_RTS) && !(tp->t_state & TS_TBLOCK)) ucomctl(sc, UMCR_RTS, DMBIS); } Static void ucom_cleanup(struct ucom_softc *sc) { DPRINTF(("ucom_cleanup: closing pipes\n")); ucom_shutdown(sc); if (sc->sc_bulkin_pipe != NULL) { usbd_abort_pipe(sc->sc_bulkin_pipe); usbd_close_pipe(sc->sc_bulkin_pipe); sc->sc_bulkin_pipe = NULL; } if (sc->sc_bulkout_pipe != NULL) { usbd_abort_pipe(sc->sc_bulkout_pipe); usbd_close_pipe(sc->sc_bulkout_pipe); sc->sc_bulkout_pipe = NULL; } if (sc->sc_ixfer != NULL) { usbd_free_xfer(sc->sc_ixfer); sc->sc_ixfer = NULL; } if (sc->sc_oxfer != NULL) { usbd_free_xfer(sc->sc_oxfer); sc->sc_oxfer = NULL; } } Static void ucomstopread(struct ucom_softc *sc) { usbd_status err; DPRINTF(("ucomstopread: enter\n")); if (!(sc->sc_state & UCS_RXSTOP)) { sc->sc_state |= UCS_RXSTOP; if (sc->sc_bulkin_pipe == NULL) { DPRINTF(("ucomstopread: bulkin pipe NULL\n")); return; } err = usbd_abort_pipe(sc->sc_bulkin_pipe); if (err) { DPRINTF(("ucomstopread: err = %s\n", usbd_errstr(err))); } } DPRINTF(("ucomstopread: leave\n")); } Index: head/sys/i386/i386/machdep.c =================================================================== --- head/sys/i386/i386/machdep.c (revision 130343) +++ head/sys/i386/i386/machdep.c (revision 130344) @@ -1,2899 +1,2899 @@ /*- * 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. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 */ #include __FBSDID("$FreeBSD$"); #include "opt_apic.h" #include "opt_atalk.h" #include "opt_compat.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_isa.h" #include "opt_kstack_pages.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_npx.h" #include "opt_perfmon.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #include #endif #include #include #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #include #ifdef PERFMON #include #endif #ifdef SMP #include #include #endif #ifdef DEV_ISA #include #endif #include #include #include #include /* Sanity check for __curthread() */ CTASSERT(offsetof(struct pcpu, pc_curthread) == 0); extern void init386(int first); extern void dblfault_handler(void); extern void printcpuinfo(void); /* XXX header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) #if !defined(CPU_ENABLE_SSE) && defined(I686_CPU) #define CPU_ENABLE_SSE #endif #if defined(CPU_DISABLE_SSE) #undef CPU_ENABLE_SSE #endif static void cpu_startup(void *); static void fpstate_drop(struct thread *td); static void get_fpcontext(struct thread *td, mcontext_t *mcp); static int set_fpcontext(struct thread *td, const mcontext_t *mcp); #ifdef CPU_ENABLE_SSE static void set_fpregs_xmm(struct save87 *, struct savexmm *); static void fill_fpregs_xmm(struct savexmm *, struct save87 *); #endif /* CPU_ENABLE_SSE */ SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) int _udatasel, _ucodesel; u_int basemem; int cold = 1; #ifdef COMPAT_43 static void osendsig(sig_t catcher, int sig, sigset_t *mask, u_long code); #endif #ifdef COMPAT_FREEBSD4 static void freebsd4_sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code); #endif long Maxmem = 0; vm_paddr_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) struct kva_md_info kmi; static struct trapframe proc0_tf; #ifndef SMP static struct pcpu __pcpu; #endif struct mtx icu_lock; static void cpu_startup(dummy) void *dummy; { /* * Good {morning,afternoon,evening,night}. */ startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %ju (%ju MB)\n", ptoa((uintmax_t)Maxmem), ptoa((uintmax_t)Maxmem) / 1048576); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { vm_paddr_t size; size = phys_avail[indx + 1] - phys_avail[indx]; printf( "0x%016jx - 0x%016jx, %ju bytes (%ju pages)\n", (uintmax_t)phys_avail[indx], (uintmax_t)phys_avail[indx + 1] - 1, (uintmax_t)size, (uintmax_t)size / PAGE_SIZE); } } vm_ksubmap_init(&kmi); printf("avail memory = %ju (%ju MB)\n", ptoa((uintmax_t)cnt.v_free_count), ptoa((uintmax_t)cnt.v_free_count) / 1048576); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); cpu_setregs(); } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top 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. */ #ifdef COMPAT_43 static void osendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct osigframe sf, *fp; struct proc *p; struct thread *td; struct sigacts *psp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct osigframe *)(td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct osigframe)); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else fp = (struct osigframe *)regs->tf_esp - 1; /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_arg2 = (register_t)&fp->sf_siginfo; sf.sf_siginfo.si_signo = sig; sf.sf_siginfo.si_code = code; sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; } else { /* Old FreeBSD-style arguments. */ sf.sf_arg2 = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* Save most if not all of trap frame. */ sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; sf.sf_siginfo.si_sc.sc_es = regs->tf_es; sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; sf.sf_siginfo.si_sc.sc_gs = rgs(); sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; /* Build the signal context to be used by osigreturn(). */ sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; sf.sf_siginfo.si_sc.sc_err = regs->tf_err; /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_siginfo.si_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* See sendsig() for comments. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(*fp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - szosigcode; regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; load_gs(_udatasel); regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } #endif /* COMPAT_43 */ #ifdef COMPAT_FREEBSD4 static void freebsd4_sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe4 sf, *sfp; struct proc *p; struct thread *td; struct sigacts *psp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe4 *)(td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct sigframe4)); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe4 *)regs->tf_esp - 1; /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - szfreebsd4_sigcode; regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } #endif /* COMPAT_FREEBSD4 */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe sf, *sfp; struct proc *p; struct thread *td; struct sigacts *psp; char *sp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); #ifdef COMPAT_FREEBSD4 if (SIGISMEMBER(psp->ps_freebsd4, sig)) { freebsd4_sendsig(catcher, sig, mask, code); return; } #endif #ifdef COMPAT_43 if (SIGISMEMBER(psp->ps_osigset, sig)) { osendsig(catcher, sig, mask, code); return; } #endif regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext); /* magic */ get_fpcontext(td, &sf.sf_uc.uc_mcontext); fpstate_drop(td); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sp = td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct sigframe); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sp = (char *)regs->tf_esp - sizeof(struct sigframe); /* Align to 16 bytes. */ sfp = (struct sigframe *)((unsigned int)sp & ~0xF); /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } /* * Build siginfo_t for SA thread */ void cpu_thread_siginfo(int sig, u_long code, siginfo_t *si) { struct proc *p; struct thread *td; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); bzero(si, sizeof(*si)); si->si_signo = sig; si->si_code = code; si->si_addr = (void *)td->td_frame->tf_err; /* XXXKSE fill other fields */ } /* * 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 * state to gain improper privileges. * * MPSAFE */ #ifdef COMPAT_43 int osigreturn(td, uap) struct thread *td; struct osigreturn_args /* { struct osigcontext *sigcntxp; } */ *uap; { struct osigcontext sc; struct trapframe *regs; struct osigcontext *scp; struct proc *p = td->td_proc; int eflags, error; regs = td->td_frame; error = copyin(uap->sigcntxp, &sc, sizeof(sc)); if (error != 0) return (error); scp = ≻ eflags = scp->sc_ps; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ if (!CS_SECURE(scp->sc_cs)) { trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; regs->tf_fs = scp->sc_fs; } /* Restore remaining registers. */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (scp->sc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif SIGSETOLD(td->td_sigmask, scp->sc_mask); SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } #endif /* COMPAT_43 */ #ifdef COMPAT_FREEBSD4 /* * MPSAFE */ int freebsd4_sigreturn(td, uap) struct thread *td; struct freebsd4_sigreturn_args /* { const ucontext4 *sigcntxp; } */ *uap; { struct ucontext4 uc; struct proc *p = td->td_proc; struct trapframe *regs; const struct ucontext4 *ucp; int cs, eflags, error; error = copyin(uap->sigcntxp, &uc, sizeof(uc)); if (error != 0) return (error); ucp = &uc; regs = td->td_frame; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("freebsd4_sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("freebsd4_sigreturn: cs = 0x%x\n", cs); trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (ucp->uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = ucp->uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } #endif /* COMPAT_FREEBSD4 */ /* * MPSAFE */ int sigreturn(td, uap) struct thread *td; struct sigreturn_args /* { const __ucontext *sigcntxp; } */ *uap; { ucontext_t uc; struct proc *p = td->td_proc; struct trapframe *regs; const ucontext_t *ucp; int cs, eflags, error, ret; error = copyin(uap->sigcntxp, &uc, sizeof(uc)); if (error != 0) return (error); ucp = &uc; regs = td->td_frame; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("sigreturn: cs = 0x%x\n", cs); trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } ret = set_fpcontext(td, &ucp->uc_mcontext); if (ret != 0) return (ret); bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (ucp->uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = ucp->uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Hook to idle the CPU when possible. In the SMP case we default to * off because a halted cpu will not currently pick up a new thread in the * run queue until the next timer tick. If turned on this will result in * approximately a 4.2% loss in real time performance in buildworld tests * (but improves user and sys times oddly enough), and saves approximately * 5% in power consumption on an idle machine (tests w/2xCPU 1.1GHz P3). * * XXX we need to have a cpu mask of idle cpus and generate an IPI or * otherwise generate some sort of interrupt to wake up cpus sitting in HLT. * Then we can have our cake and eat it too. * * XXX I'm turning it on for SMP as well by default for now. It seems to * help lock contention somewhat, and this is critical for HTT. -Peter */ static int cpu_idle_hlt = 1; SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW, &cpu_idle_hlt, 0, "Idle loop HLT enable"); static void cpu_idle_default(void) { /* * we must absolutely guarentee that hlt is the * absolute next instruction after sti or we * introduce a timing window. */ __asm __volatile("sti; hlt"); } /* * Note that we have to be careful here to avoid a race between checking * sched_runnable() and actually halting. If we don't do this, we may waste * the time between calling hlt and the next interrupt even though there * is a runnable process. */ void cpu_idle(void) { #ifdef SMP if (mp_grab_cpu_hlt()) return; #endif if (cpu_idle_hlt) { disable_intr(); if (sched_runnable()) enable_intr(); else (*cpu_idle_hook)(); } } /* Other subsystems (e.g., ACPI) can hook this later. */ void (*cpu_idle_hook)(void) = cpu_idle_default; /* * Clear registers on exec */ void exec_setregs(td, entry, stack, ps_strings) struct thread *td; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = td->td_frame; struct pcb *pcb = td->td_pcb; /* Reset pc->pcb_gs and %gs before possibly invalidating it. */ pcb->pcb_gs = _udatasel; load_gs(_udatasel); if (td->td_proc->p_md.md_ldt) user_ldt_free(td); bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* * Reset the hardware debug registers if they were in use. * They won't have any meaning for the newly exec'd process. */ if (pcb->pcb_flags & PCB_DBREGS) { pcb->pcb_dr0 = 0; pcb->pcb_dr1 = 0; pcb->pcb_dr2 = 0; pcb->pcb_dr3 = 0; pcb->pcb_dr6 = 0; pcb->pcb_dr7 = 0; if (pcb == PCPU_GET(curpcb)) { /* * Clear the debug registers on the running * CPU, otherwise they will end up affecting * the next process we switch to. */ reset_dbregs(); } pcb->pcb_flags &= ~PCB_DBREGS; } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ td->td_pcb->pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); /* Initialize the npx (if any) for the current process. */ /* * XXX the above load_cr0() also initializes it and is a layering * violation if NPX is configured. It drops the npx partially * and this would be fatal if we were interrupted now, and decided * to force the state to the pcb, and checked the invariant * (CR0_TS clear) if and only if PCPU_GET(fpcurthread) != NULL). * ALL of this can happen except the check. The check used to * happen and be fatal later when we didn't complete the drop * before returning to user mode. This should be fixed properly * soon. */ fpstate_drop(td); /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ td->td_retval[1] = 0; } void cpu_setregs(void) { unsigned int cr0; cr0 = rcr0(); #ifdef SMP cr0 |= CR0_NE; /* Done by npxinit() */ #endif cr0 |= CR0_MP | CR0_TS; /* Done at every execve() too. */ #ifndef I386_CPU cr0 |= CR0_WP | CR0_AM; #endif load_cr0(cr0); load_gs(_udatasel); } static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); u_long bootdev; /* not a dev_t - encoding is different */ SYSCTL_ULONG(_machdep, OID_AUTO, guessed_bootdev, CTLFLAG_RD, &bootdev, 0, "Maybe the Boot device (not in dev_t format)"); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ struct region_descriptor r_gdt, r_idt; /* table descriptors */ int private_tss; /* flag indicating private tss */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0uarea; extern vm_offset_t proc0kstack; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPRIV_SEL 3 SMP Per-Processor Private Data Descriptor */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 4 Proc 0 Tss Descriptor */ { 0x0, /* segment base address */ sizeof(struct i386tss)-1,/* length */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 5 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 6 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 7 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSLOWMEM_SEL 8 BIOS access to realmode segment 0x40, must be #8 in GDT */ { 0x400, /* segment base address */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 9 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE32_SEL 10 BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE16_SEL 11 BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSDATA_SEL 12 BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSUTIL_SEL 13 BIOS 16-bit interface (Utility) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSARGS_SEL 14 BIOS 16-bit interface (Arguments) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(xmm), IDTVEC(lcall_syscall), IDTVEC(int0x80_syscall); #ifdef DDB /* * Display the index and function name of any IDT entries that don't use * the default 'rsvd' entry point. */ DB_SHOW_COMMAND(idt, db_show_idt) { struct gate_descriptor *ip; int idx, quit; uintptr_t func; ip = idt; db_setup_paging(db_simple_pager, &quit, DB_LINES_PER_PAGE); for (idx = 0, quit = 0; idx < NIDT; idx++) { func = (ip->gd_hioffset << 16 | ip->gd_looffset); if (func != (uintptr_t)&IDTVEC(rsvd)) { db_printf("%3d\t", idx); db_printsym(func, DB_STGY_PROC); db_printf("\n"); } ip++; } } #endif void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } #define PHYSMAP_SIZE (2 * 8) /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. * * XXX first should be vm_paddr_t. */ static void getmemsize(int first) { int i, physmap_idx, pa_indx; int hasbrokenint12; u_int extmem; struct vm86frame vmf; struct vm86context vmc; vm_paddr_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t *pte; char *cp; struct bios_smap *smap; hasbrokenint12 = 0; TUNABLE_INT_FETCH("hw.hasbrokenint12", &hasbrokenint12); bzero(&vmf, sizeof(vmf)); bzero(physmap, sizeof(physmap)); basemem = 0; /* * Some newer BIOSes has broken INT 12H implementation which cause * kernel panic immediately. In this case, we need to scan SMAP * with INT 15:E820 first, then determine base memory size. */ if (hasbrokenint12) { goto int15e820; } /* * Perform "base memory" related probes & setup */ vm86_intcall(0x12, &vmf); basemem = vmf.vmf_ax; if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) pmap_kenter(KERNBASE + pa, pa); /* * Map pages between basemem and ISA_HOLE_START, if any, r/w into * the vm86 page table so that vm86 can scribble on them using * the vm86 map too. XXX: why 2 ways for this and only 1 way for * page 0, at least as initialized here? */ pte = (pt_entry_t *)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; int15e820: /* * map page 1 R/W into the kernel page table so we can use it * as a buffer. The kernel will unmap this page later. */ pmap_kenter(KERNBASE + (1 << PAGE_SHIFT), 1 << PAGE_SHIFT); /* * get memory map with INT 15:E820 */ vmc.npages = 0; smap = (void *)vm86_addpage(&vmc, 1, KERNBASE + (1 << PAGE_SHIFT)); vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di); physmap_idx = 0; vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = sizeof(struct bios_smap); i = vm86_datacall(0x15, &vmf, &vmc); if (i || vmf.vmf_eax != SMAP_SIG) break; if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%016llx len=%016llx\n", smap->type, smap->base, smap->length); if (smap->type != 0x01) goto next_run; if (smap->length == 0) goto next_run; #ifndef PAE if (smap->base >= 0xffffffff) { printf("%uK of memory above 4GB ignored\n", (u_int)(smap->length / 1024)); goto next_run; } #endif for (i = 0; i <= physmap_idx; i += 2) { if (smap->base < physmap[i + 1]) { if (boothowto & RB_VERBOSE) printf( "Overlapping or non-montonic memory region, ignoring second region\n"); goto next_run; } } if (smap->base == physmap[physmap_idx + 1]) { physmap[physmap_idx + 1] += smap->length; goto next_run; } physmap_idx += 2; if (physmap_idx == PHYSMAP_SIZE) { printf( "Too many segments in the physical address map, giving up\n"); break; } physmap[physmap_idx] = smap->base; physmap[physmap_idx + 1] = smap->base + smap->length; next_run: ; } while (vmf.vmf_ebx != 0); /* * Perform "base memory" related probes & setup based on SMAP */ if (basemem == 0) { for (i = 0; i <= physmap_idx; i += 2) { if (physmap[i] == 0x00000000) { basemem = physmap[i + 1] / 1024; break; } } /* * XXX this function is horribly organized and has to the same * things that it does above here. */ if (basemem == 0) basemem = 640; if (basemem > 640) { printf( "Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * Let vm86 scribble on pages between basemem and * ISA_HOLE_START, as above. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) pmap_kenter(KERNBASE + pa, pa); pte = (pt_entry_t *)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; } if (physmap[1] != 0) goto physmap_done; /* * If we failed above, try memory map with INT 15:E801 */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { extmem = vmf.vmf_cx + vmf.vmf_dx * 64; } else { #if 0 vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); extmem = vmf.vmf_ax; #else /* * Prefer the RTC value for extended memory. */ extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8); #endif } /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. * * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((extmem > 15 * 1024) && (extmem < 16 * 1024)) extmem = 15 * 1024; physmap[0] = 0; physmap[1] = basemem * 1024; physmap_idx = 2; physmap[physmap_idx] = 0x100000; physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; physmap_done: /* * Now, physmap contains a map of physical memory. */ #ifdef SMP /* make hole for AP bootstrap code */ physmap[1] = mp_bootaddress(physmap[1]); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif /* * hw.physmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ if ((cp = getenv("hw.physmem")) != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(cp, &ep, 0); if ((ep != cp) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", cp); else Maxmem = atop(AllowMem); freeenv(cp); } if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %ldK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa((vm_paddr_t)Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(first, 0); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; pte = CMAP1; /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_paddr_t end; end = ptoa((vm_paddr_t)Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad; int *ptr = (int *)CADDR1; /* * block out kernel memory as not available. */ if (pa >= KERNLOAD && pa < first) continue; page_bad = FALSE; /* * map page into kernel: valid, read/write,non-cacheable */ *pte = pa | PG_V | PG_RW | PG_N; invltlb(); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) { page_bad = TRUE; } /* * Restore original value. */ *(int *)ptr = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) { continue; } /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf( "Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; } } *pte = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; } void init386(first) int first; { struct gate_descriptor *gdp; int gsel_tss, metadata_missing, off, x; struct pcpu *pc; proc0.p_uarea = proc0uarea; thread0.td_kstack = proc0kstack; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; /* * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ proc_linkup(&proc0, &ksegrp0, &kse0, &thread0); metadata_missing = 0; if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } else { metadata_missing = 1; } if (envmode == 1) kern_envp = static_env; else if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* Init basic tunables, hz etc */ init_param1(); /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = atop(0 - 1); gdt_segs[GDATA_SEL].ssd_limit = atop(0 - 1); #ifdef SMP pc = &SMP_prvspace[0].pcpu; gdt_segs[GPRIV_SEL].ssd_limit = atop(sizeof(struct privatespace) - 1); #else pc = &__pcpu; gdt_segs[GPRIV_SEL].ssd_limit = atop(sizeof(struct pcpu) - 1); #endif gdt_segs[GPRIV_SEL].ssd_base = (int) pc; gdt_segs[GPROC0_SEL].ssd_base = (int) &pc->pc_common_tss; for (x = 0; x < NGDT; x++) ssdtosd(&gdt_segs[x], &gdt[x].sd); r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); pcpu_init(pc, 0, sizeof(struct pcpu)); PCPU_SET(prvspace, pc); PCPU_SET(curthread, &thread0); PCPU_SET(curpcb, thread0.td_pcb); /* * Initialize mutexes. * * icu_lock: in order to allow an interrupt to occur in a critical * section, to set pcpu->ipending (etc...) properly, we * must be able to get the icu lock, so it can't be * under witness. */ mutex_init(); mtx_init(&clock_lock, "clk", NULL, MTX_SPIN); mtx_init(&icu_lock, "icu", NULL, MTX_SPIN | MTX_NOWITNESS); /* make ldt memory segments */ /* * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ ldt_segs[LUCODE_SEL].ssd_limit = atop(VM_MAXUSER_ADDRESS - 1); ldt_segs[LUDATA_SEL].ssd_limit = atop(VM_MAXUSER_ADDRESS - 1); for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DE, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DB, &IDTVEC(dbg), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NMI, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_BP, &IDTVEC(bpt), SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_OF, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_BR, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_UD, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NM, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL , GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DF, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(IDT_FPUGP, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_TS, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NP, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_SS, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_GP, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_PF, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_MF, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_AC, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_MC, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_XF, &IDTVEC(xmm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); /* * Initialize the console before we print anything out. */ cninit(); if (metadata_missing) printf("WARNING: loader(8) metadata is missing!\n"); #ifdef DEV_ISA atpic_startup(); #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(IDT_UD, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_GP, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ /* Note: -16 is so we can grow the trapframe if we came from vm86 */ PCPU_SET(common_tss.tss_esp0, thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE - sizeof(struct pcb) - 16); PCPU_SET(common_tss.tss_ss0, GSEL(GDATA_SEL, SEL_KPL)); gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); private_tss = 0; PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); PCPU_SET(common_tss.tss_ioopt, (sizeof (struct i386tss)) << 16); ltr(gsel_tss); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int)&dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); #ifdef PAE dblfault_tss.tss_cr3 = (int)IdlePDPT; #else dblfault_tss.tss_cr3 = (int)IdlePTD; #endif dblfault_tss.tss_eip = (int)dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_fs = GSEL(GPRIV_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); vm86_initialize(); getmemsize(first); init_param2(physmem); /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(lcall_syscall); gdp->gd_looffset = x; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = x >> 16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ thread0.td_pcb->pcb_flags = 0; /* XXXKSE */ #ifdef PAE thread0.td_pcb->pcb_cr3 = (int)IdlePDPT; #else thread0.td_pcb->pcb_cr3 = (int)IdlePTD; #endif thread0.td_pcb->pcb_ext = 0; thread0.td_frame = &proc0_tf; } void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) { pcpu->pc_acpi_id = 0xffffffff; } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL) static void f00f_hack(void *unused) { struct gate_descriptor *new_idt; vm_offset_t tmp; if (!has_f00f_bug) return; GIANT_REQUIRED; printf("Intel Pentium detected, installing workaround for F00F bug\n"); tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); /* Put the problematic entry (#6) at the end of the lower page. */ new_idt = (struct gate_descriptor*) (tmp + PAGE_SIZE - 7 * sizeof(struct gate_descriptor)); bcopy(idt, new_idt, sizeof(idt0)); r_idt.rd_base = (u_int)new_idt; lidt(&r_idt); idt = new_idt; if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(struct thread *td, u_long addr) { td->td_frame->tf_eip = addr; return (0); } int ptrace_single_step(struct thread *td) { td->td_frame->tf_eflags |= PSL_T; return (0); } int fill_regs(struct thread *td, struct reg *regs) { struct pcb *pcb; struct trapframe *tp; tp = td->td_frame; regs->r_fs = tp->tf_fs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = td->td_pcb; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(struct thread *td, struct reg *regs) { struct pcb *pcb; struct trapframe *tp; tp = td->td_frame; if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_fs = regs->r_fs; tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = td->td_pcb; pcb->pcb_gs = regs->r_gs; return (0); } #ifdef CPU_ENABLE_SSE static void fill_fpregs_xmm(sv_xmm, sv_87) struct savexmm *sv_xmm; struct save87 *sv_87; { register struct env87 *penv_87 = &sv_87->sv_env; register struct envxmm *penv_xmm = &sv_xmm->sv_env; int i; bzero(sv_87, sizeof(*sv_87)); /* FPU control/status */ penv_87->en_cw = penv_xmm->en_cw; penv_87->en_sw = penv_xmm->en_sw; penv_87->en_tw = penv_xmm->en_tw; penv_87->en_fip = penv_xmm->en_fip; penv_87->en_fcs = penv_xmm->en_fcs; penv_87->en_opcode = penv_xmm->en_opcode; penv_87->en_foo = penv_xmm->en_foo; penv_87->en_fos = penv_xmm->en_fos; /* FPU registers */ for (i = 0; i < 8; ++i) sv_87->sv_ac[i] = sv_xmm->sv_fp[i].fp_acc; } static void set_fpregs_xmm(sv_87, sv_xmm) struct save87 *sv_87; struct savexmm *sv_xmm; { register struct env87 *penv_87 = &sv_87->sv_env; register struct envxmm *penv_xmm = &sv_xmm->sv_env; int i; /* FPU control/status */ penv_xmm->en_cw = penv_87->en_cw; penv_xmm->en_sw = penv_87->en_sw; penv_xmm->en_tw = penv_87->en_tw; penv_xmm->en_fip = penv_87->en_fip; penv_xmm->en_fcs = penv_87->en_fcs; penv_xmm->en_opcode = penv_87->en_opcode; penv_xmm->en_foo = penv_87->en_foo; penv_xmm->en_fos = penv_87->en_fos; /* FPU registers */ for (i = 0; i < 8; ++i) sv_xmm->sv_fp[i].fp_acc = sv_87->sv_ac[i]; } #endif /* CPU_ENABLE_SSE */ int fill_fpregs(struct thread *td, struct fpreg *fpregs) { #ifdef CPU_ENABLE_SSE if (cpu_fxsr) { fill_fpregs_xmm(&td->td_pcb->pcb_save.sv_xmm, (struct save87 *)fpregs); return (0); } #endif /* CPU_ENABLE_SSE */ bcopy(&td->td_pcb->pcb_save.sv_87, fpregs, sizeof *fpregs); return (0); } int set_fpregs(struct thread *td, struct fpreg *fpregs) { #ifdef CPU_ENABLE_SSE if (cpu_fxsr) { set_fpregs_xmm((struct save87 *)fpregs, &td->td_pcb->pcb_save.sv_xmm); return (0); } #endif /* CPU_ENABLE_SSE */ bcopy(fpregs, &td->td_pcb->pcb_save.sv_87, sizeof *fpregs); return (0); } /* * Get machine context. */ int get_mcontext(struct thread *td, mcontext_t *mcp, int flags) { struct trapframe *tp; tp = td->td_frame; PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(tp->tf_esp); PROC_UNLOCK(curthread->td_proc); mcp->mc_gs = td->td_pcb->pcb_gs; mcp->mc_fs = tp->tf_fs; mcp->mc_es = tp->tf_es; mcp->mc_ds = tp->tf_ds; mcp->mc_edi = tp->tf_edi; mcp->mc_esi = tp->tf_esi; mcp->mc_ebp = tp->tf_ebp; mcp->mc_isp = tp->tf_isp; if (flags & GET_MC_CLEAR_RET) { mcp->mc_eax = 0; mcp->mc_edx = 0; } else { mcp->mc_eax = tp->tf_eax; mcp->mc_edx = tp->tf_edx; } mcp->mc_ebx = tp->tf_ebx; mcp->mc_ecx = tp->tf_ecx; mcp->mc_eip = tp->tf_eip; mcp->mc_cs = tp->tf_cs; mcp->mc_eflags = tp->tf_eflags; mcp->mc_esp = tp->tf_esp; mcp->mc_ss = tp->tf_ss; mcp->mc_len = sizeof(*mcp); get_fpcontext(td, mcp); return (0); } /* * Set machine context. * * However, we don't set any but the user modifiable flags, and we won't * touch the cs selector. */ int set_mcontext(struct thread *td, const mcontext_t *mcp) { struct trapframe *tp; int eflags, ret; tp = td->td_frame; if (mcp->mc_len != sizeof(*mcp)) return (EINVAL); eflags = (mcp->mc_eflags & PSL_USERCHANGE) | (tp->tf_eflags & ~PSL_USERCHANGE); if ((ret = set_fpcontext(td, mcp)) == 0) { tp->tf_fs = mcp->mc_fs; tp->tf_es = mcp->mc_es; tp->tf_ds = mcp->mc_ds; tp->tf_edi = mcp->mc_edi; tp->tf_esi = mcp->mc_esi; tp->tf_ebp = mcp->mc_ebp; tp->tf_ebx = mcp->mc_ebx; tp->tf_edx = mcp->mc_edx; tp->tf_ecx = mcp->mc_ecx; tp->tf_eax = mcp->mc_eax; tp->tf_eip = mcp->mc_eip; tp->tf_eflags = eflags; tp->tf_esp = mcp->mc_esp; tp->tf_ss = mcp->mc_ss; td->td_pcb->pcb_gs = mcp->mc_gs; ret = 0; } return (ret); } static void get_fpcontext(struct thread *td, mcontext_t *mcp) { #ifndef DEV_NPX mcp->mc_fpformat = _MC_FPFMT_NODEV; mcp->mc_ownedfp = _MC_FPOWNED_NONE; #else union savefpu *addr; /* * XXX mc_fpstate might be misaligned, since its declaration is not * unportabilized using __attribute__((aligned(16))) like the * declaration of struct savemm, and anyway, alignment doesn't work * for auto variables since we don't use gcc's pessimal stack * alignment. Work around this by abusing the spare fields after * mcp->mc_fpstate. * * XXX unpessimize most cases by only aligning when fxsave might be * called, although this requires knowing too much about * npxgetregs()'s internals. */ addr = (union savefpu *)&mcp->mc_fpstate; if (td == PCPU_GET(fpcurthread) && #ifdef CPU_ENABLE_SSE cpu_fxsr && #endif ((uintptr_t)(void *)addr & 0xF)) { do addr = (void *)((char *)addr + 4); while ((uintptr_t)(void *)addr & 0xF); } mcp->mc_ownedfp = npxgetregs(td, addr); if (addr != (union savefpu *)&mcp->mc_fpstate) { bcopy(addr, &mcp->mc_fpstate, sizeof(mcp->mc_fpstate)); bzero(&mcp->mc_spare2, sizeof(mcp->mc_spare2)); } mcp->mc_fpformat = npxformat(); #endif } static int set_fpcontext(struct thread *td, const mcontext_t *mcp) { union savefpu *addr; if (mcp->mc_fpformat == _MC_FPFMT_NODEV) return (0); else if (mcp->mc_fpformat != _MC_FPFMT_387 && mcp->mc_fpformat != _MC_FPFMT_XMM) return (EINVAL); else if (mcp->mc_ownedfp == _MC_FPOWNED_NONE) /* We don't care what state is left in the FPU or PCB. */ fpstate_drop(td); else if (mcp->mc_ownedfp == _MC_FPOWNED_FPU || mcp->mc_ownedfp == _MC_FPOWNED_PCB) { /* XXX align as above. */ addr = (union savefpu *)&mcp->mc_fpstate; if (td == PCPU_GET(fpcurthread) && #ifdef CPU_ENABLE_SSE cpu_fxsr && #endif ((uintptr_t)(void *)addr & 0xF)) { do addr = (void *)((char *)addr + 4); while ((uintptr_t)(void *)addr & 0xF); bcopy(&mcp->mc_fpstate, addr, sizeof(mcp->mc_fpstate)); } #ifdef DEV_NPX /* * XXX we violate the dubious requirement that npxsetregs() * be called with interrupts disabled. */ npxsetregs(td, addr); #endif /* * Don't bother putting things back where they were in the * misaligned case, since we know that the caller won't use * them again. */ } else return (EINVAL); return (0); } static void fpstate_drop(struct thread *td) { register_t s; s = intr_disable(); #ifdef DEV_NPX if (PCPU_GET(fpcurthread) == td) npxdrop(); #endif /* * XXX force a full drop of the npx. The above only drops it if we * owned it. npxgetregs() has the same bug in the !cpu_fxsr case. * * XXX I don't much like npxgetregs()'s semantics of doing a full * drop. Dropping only to the pcb matches fnsave's behaviour. * We only need to drop to !PCB_INITDONE in sendsig(). But * sendsig() is the only caller of npxgetregs()... perhaps we just * have too many layers. */ curthread->td_pcb->pcb_flags &= ~PCB_NPXINITDONE; intr_restore(s); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { struct pcb *pcb; if (td == NULL) { dbregs->dr[0] = rdr0(); dbregs->dr[1] = rdr1(); dbregs->dr[2] = rdr2(); dbregs->dr[3] = rdr3(); dbregs->dr[4] = rdr4(); dbregs->dr[5] = rdr5(); dbregs->dr[6] = rdr6(); dbregs->dr[7] = rdr7(); } else { pcb = td->td_pcb; dbregs->dr[0] = pcb->pcb_dr0; dbregs->dr[1] = pcb->pcb_dr1; dbregs->dr[2] = pcb->pcb_dr2; dbregs->dr[3] = pcb->pcb_dr3; dbregs->dr[4] = 0; dbregs->dr[5] = 0; dbregs->dr[6] = pcb->pcb_dr6; dbregs->dr[7] = pcb->pcb_dr7; } return (0); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { struct pcb *pcb; int i; u_int32_t mask1, mask2; if (td == NULL) { load_dr0(dbregs->dr[0]); load_dr1(dbregs->dr[1]); load_dr2(dbregs->dr[2]); load_dr3(dbregs->dr[3]); load_dr4(dbregs->dr[4]); load_dr5(dbregs->dr[5]); load_dr6(dbregs->dr[6]); load_dr7(dbregs->dr[7]); } else { /* * Don't let an illegal value for dr7 get set. Specifically, * check for undefined settings. Setting these bit patterns * result in undefined behaviour and can lead to an unexpected * TRCTRAP. */ for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; i++, mask1 <<= 2, mask2 <<= 2) if ((dbregs->dr[7] & mask1) == mask2) return (EINVAL); pcb = td->td_pcb; /* * Don't let a process set a breakpoint that is not within the * process's address space. If a process could do this, it * could halt the system by setting a breakpoint in the kernel * (if ddb was enabled). Thus, we need to check to make sure * that no breakpoints are being enabled for addresses outside * process's address space, unless, perhaps, we were called by * uid 0. * * XXX - what about when the watched area of the user's * address space is written into from within the kernel * ... wouldn't that still cause a breakpoint to be generated * from within kernel mode? */ if (suser(td) != 0) { if (dbregs->dr[7] & 0x3) { /* dr0 is enabled */ if (dbregs->dr[0] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<2)) { /* dr1 is enabled */ if (dbregs->dr[1] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<4)) { /* dr2 is enabled */ if (dbregs->dr[2] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<6)) { /* dr3 is enabled */ if (dbregs->dr[3] >= VM_MAXUSER_ADDRESS) return (EINVAL); } } pcb->pcb_dr0 = dbregs->dr[0]; pcb->pcb_dr1 = dbregs->dr[1]; pcb->pcb_dr2 = dbregs->dr[2]; pcb->pcb_dr3 = dbregs->dr[3]; pcb->pcb_dr6 = dbregs->dr[6]; pcb->pcb_dr7 = dbregs->dr[7]; pcb->pcb_flags |= PCB_DBREGS; } return (0); } /* * Return > 0 if a hardware breakpoint has been hit, and the * breakpoint was in user space. Return 0, otherwise. */ int user_dbreg_trap(void) { u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ u_int32_t bp; /* breakpoint bits extracted from dr6 */ int nbp; /* number of breakpoints that triggered */ caddr_t addr[4]; /* breakpoint addresses */ int i; dr7 = rdr7(); if ((dr7 & 0x000000ff) == 0) { /* * all GE and LE bits in the dr7 register are zero, * thus the trap couldn't have been caused by the * hardware debug registers */ return 0; } nbp = 0; dr6 = rdr6(); bp = dr6 & 0x0000000f; if (!bp) { /* * None of the breakpoint bits are set meaning this * trap was not caused by any of the debug registers */ return 0; } /* * at least one of the breakpoints were hit, check to see * which ones and if any of them are user space addresses */ if (bp & 0x01) { addr[nbp++] = (caddr_t)rdr0(); } if (bp & 0x02) { addr[nbp++] = (caddr_t)rdr1(); } if (bp & 0x04) { addr[nbp++] = (caddr_t)rdr2(); } if (bp & 0x08) { addr[nbp++] = (caddr_t)rdr3(); } for (i=0; i /* * Provide stub functions so that the MADT APIC enumerator in the acpi * kernel module will link against a kernel without 'device apic'. * * XXX - This is a gross hack. */ void apic_register_enumerator(struct apic_enumerator *enumerator) { } void * ioapic_create(uintptr_t addr, int32_t id, int intbase) { return (NULL); } int ioapic_disable_pin(void *cookie, u_int pin) { return (ENXIO); } void ioapic_enable_mixed_mode(void) { } int ioapic_get_vector(void *cookie, u_int pin) { return (-1); } void ioapic_register(void *cookie) { } int ioapic_remap_vector(void *cookie, u_int pin, int vector) { return (ENXIO); } int ioapic_set_extint(void *cookie, u_int pin) { return (ENXIO); } int ioapic_set_nmi(void *cookie, u_int pin) { return (ENXIO); } int ioapic_set_polarity(void *cookie, u_int pin, enum intr_polarity pol) { return (ENXIO); } int ioapic_set_triggermode(void *cookie, u_int pin, enum intr_trigger trigger) { return (ENXIO); } void lapic_create(u_int apic_id, int boot_cpu) { } void lapic_init(uintptr_t addr) { } int lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode) { return (ENXIO); } int lapic_set_lvt_polarity(u_int apic_id, u_int lvt, enum intr_polarity pol) { return (ENXIO); } int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, enum intr_trigger trigger) { return (ENXIO); } #endif #ifdef DDB /* * Provide inb() and outb() as functions. They are normally only * available as macros calling inlined functions, thus cannot be * called inside DDB. * * The actual code is stolen from , and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/ia64/ia64/machdep.c =================================================================== --- head/sys/ia64/ia64/machdep.c (revision 130343) +++ head/sys/ia64/ia64/machdep.c (revision 130344) @@ -1,1378 +1,1378 @@ /*- * Copyright (c) 2003,2004 Marcel Moolenaar * Copyright (c) 2000,2001 Doug Rabson * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include "opt_compat.h" #include "opt_ddb.h" #include "opt_kstack_pages.h" #include "opt_msgbuf.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SMP #include #endif #include #include #include #include #include #include #include #include #include #include #include u_int64_t processor_frequency; u_int64_t bus_frequency; u_int64_t itc_frequency; int cold = 1; u_int64_t pa_bootinfo; struct bootinfo bootinfo; struct pcpu early_pcpu; extern char kstack[]; struct user *proc0uarea; vm_offset_t proc0kstack; extern u_int64_t kernel_text[], _end[]; extern u_int64_t ia64_gateway_page[]; extern u_int64_t break_sigtramp[]; extern u_int64_t epc_sigtramp[]; FPSWA_INTERFACE *fpswa_interface; u_int64_t ia64_pal_base; u_int64_t ia64_port_base; char machine[] = MACHINE; SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD, machine, 0, ""); static char cpu_model[64]; SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD, cpu_model, 0, "The CPU model name"); static char cpu_family[64]; SYSCTL_STRING(_hw, OID_AUTO, family, CTLFLAG_RD, cpu_family, 0, "The CPU family name"); #ifdef DDB /* start and end of kernel symbol table */ void *ksym_start, *ksym_end; #endif static void cpu_startup(void *); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) struct msgbuf *msgbufp=0; long Maxmem = 0; vm_offset_t phys_avail[100]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) void mi_startup(void); /* XXX should be in a MI header */ struct kva_md_info kmi; #define Mhz 1000000L #define Ghz (1000L*Mhz) static void identifycpu(void) { char vendor[17]; char *family_name, *model_name; u_int64_t features, tmp; int number, revision, model, family, archrev; /* * Assumes little-endian. */ *(u_int64_t *) &vendor[0] = ia64_get_cpuid(0); *(u_int64_t *) &vendor[8] = ia64_get_cpuid(1); vendor[16] = '\0'; tmp = ia64_get_cpuid(3); number = (tmp >> 0) & 0xff; revision = (tmp >> 8) & 0xff; model = (tmp >> 16) & 0xff; family = (tmp >> 24) & 0xff; archrev = (tmp >> 32) & 0xff; family_name = model_name = "unknown"; switch (family) { case 0x07: family_name = "Itanium"; model_name = "Merced"; break; case 0x1f: family_name = "Itanium 2"; switch (model) { case 0x00: model_name = "McKinley"; break; case 0x01: /* * Deerfield is a low-voltage variant based on the * Madison core. We need circumstantial evidence * (i.e. the clock frequency) to identify those. * Allow for roughly 1% error margin. */ tmp = processor_frequency >> 7; if ((processor_frequency - tmp) < 1*Ghz && (processor_frequency + tmp) >= 1*Ghz) model_name = "Deerfield"; else model_name = "Madison"; break; } break; } snprintf(cpu_family, sizeof(cpu_family), "%s", family_name); snprintf(cpu_model, sizeof(cpu_model), "%s", model_name); features = ia64_get_cpuid(4); printf("CPU: %s (", model_name); if (processor_frequency) { printf("%ld.%02ld-Mhz ", (processor_frequency + 4999) / Mhz, ((processor_frequency + 4999) / (Mhz/100)) % 100); } printf("%s)\n", family_name); printf(" Origin = \"%s\" Revision = %d\n", vendor, revision); printf(" Features = 0x%b\n", (u_int32_t) features, "\020" "\001LB" /* long branch (brl) instruction. */ "\002SD" /* Spontaneous deferral. */ "\003AO" /* 16-byte atomic operations (ld, st, cmpxchg). */ ); } static void cpu_startup(dummy) void *dummy; { /* * Good {morning,afternoon,evening,night}. */ identifycpu(); /* startrtclock(); */ #ifdef PERFMON perfmon_init(); #endif printf("real memory = %ld (%ld MB)\n", ia64_ptob(Maxmem), ia64_ptob(Maxmem) / 1048576); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { int size1 = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08lx - 0x%08lx, %d bytes (%d pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE); } } vm_ksubmap_init(&kmi); printf("avail memory = %ld (%ld MB)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1048576); if (fpswa_interface == NULL) printf("Warning: no FPSWA package supplied\n"); else printf("FPSWA Revision = 0x%lx, Entry = %p\n", (long)fpswa_interface->Revision, (void *)fpswa_interface->Fpswa); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); /* * Traverse the MADT to discover IOSAPIC and Local SAPIC * information. */ ia64_probe_sapics(); ia64_mca_init(); } void cpu_boot(int howto) { ia64_efi_runtime->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, 0); } void cpu_halt() { ia64_efi_runtime->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, 0); } static void cpu_idle_default(void) { struct ia64_pal_result res; res = ia64_call_pal_static(PAL_HALT_LIGHT, 0, 0, 0); } void cpu_idle() { (*cpu_idle_hook)(); } /* Other subsystems (e.g., ACPI) can hook this later. */ void (*cpu_idle_hook)(void) = cpu_idle_default; void cpu_reset() { cpu_boot(0); } void cpu_switch(struct thread *old, struct thread *new) { struct pcb *oldpcb, *newpcb; oldpcb = old->td_pcb; #if IA32 ia32_savectx(oldpcb); #endif if (PCPU_GET(fpcurthread) == old) old->td_frame->tf_special.psr |= IA64_PSR_DFH; if (!savectx(oldpcb)) { newpcb = new->td_pcb; oldpcb->pcb_current_pmap = pmap_switch(newpcb->pcb_current_pmap); PCPU_SET(curthread, new); #if IA32 ia32_restorectx(newpcb); #endif if (PCPU_GET(fpcurthread) == new) new->td_frame->tf_special.psr &= ~IA64_PSR_DFH; restorectx(newpcb); /* We should not get here. */ panic("cpu_switch: restorectx() returned"); /* NOTREACHED */ } } void cpu_throw(struct thread *old __unused, struct thread *new) { struct pcb *newpcb; newpcb = new->td_pcb; (void)pmap_switch(newpcb->pcb_current_pmap); PCPU_SET(curthread, new); #if IA32 ia32_restorectx(newpcb); #endif restorectx(newpcb); /* We should not get here. */ panic("cpu_throw: restorectx() returned"); /* NOTREACHED */ } void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) { size_t pcpusz; /* * Make sure the PCB is 16-byte aligned by making the PCPU * a multiple of 16 bytes. We assume the PCPU is 16-byte * aligned itself. */ pcpusz = (sizeof(struct pcpu) + 15) & ~15; KASSERT(size >= pcpusz + sizeof(struct pcb), ("%s: too small an allocation for pcpu", __func__)); pcpu->pc_pcb = (struct pcb *)((char*)pcpu + pcpusz); pcpu->pc_acpi_id = cpuid; } void map_pal_code(void) { struct ia64_pte pte; u_int64_t psr; if (ia64_pal_base == 0) return; bzero(&pte, sizeof(pte)); pte.pte_p = 1; pte.pte_ma = PTE_MA_WB; pte.pte_a = 1; pte.pte_d = 1; pte.pte_pl = PTE_PL_KERN; pte.pte_ar = PTE_AR_RWX; pte.pte_ppn = ia64_pal_base >> 12; __asm __volatile("ptr.d %0,%1; ptr.i %0,%1" :: "r"(IA64_PHYS_TO_RR7(ia64_pal_base)), "r"(IA64_ID_PAGE_SHIFT<<2)); __asm __volatile("mov %0=psr" : "=r"(psr)); __asm __volatile("rsm psr.ic|psr.i"); __asm __volatile("srlz.i"); __asm __volatile("mov cr.ifa=%0" :: "r"(IA64_PHYS_TO_RR7(ia64_pal_base))); __asm __volatile("mov cr.itir=%0" :: "r"(IA64_ID_PAGE_SHIFT << 2)); __asm __volatile("itr.d dtr[%0]=%1" :: "r"(1), "r"(*(u_int64_t*)&pte)); __asm __volatile("srlz.d"); /* XXX not needed. */ __asm __volatile("itr.i itr[%0]=%1" :: "r"(1), "r"(*(u_int64_t*)&pte)); __asm __volatile("mov psr.l=%0" :: "r" (psr)); __asm __volatile("srlz.i"); } void map_gateway_page(void) { struct ia64_pte pte; u_int64_t psr; bzero(&pte, sizeof(pte)); pte.pte_p = 1; pte.pte_ma = PTE_MA_WB; pte.pte_a = 1; pte.pte_d = 1; pte.pte_pl = PTE_PL_KERN; pte.pte_ar = PTE_AR_X_RX; pte.pte_ppn = IA64_RR_MASK((u_int64_t)ia64_gateway_page) >> 12; __asm __volatile("ptr.d %0,%1; ptr.i %0,%1" :: "r"(VM_MAX_ADDRESS), "r"(PAGE_SHIFT << 2)); __asm __volatile("mov %0=psr" : "=r"(psr)); __asm __volatile("rsm psr.ic|psr.i"); __asm __volatile("srlz.i"); __asm __volatile("mov cr.ifa=%0" :: "r"(VM_MAX_ADDRESS)); __asm __volatile("mov cr.itir=%0" :: "r"(PAGE_SHIFT << 2)); __asm __volatile("itr.d dtr[%0]=%1" :: "r"(3), "r"(*(u_int64_t*)&pte)); __asm __volatile("srlz.d"); /* XXX not needed. */ __asm __volatile("itr.i itr[%0]=%1" :: "r"(3), "r"(*(u_int64_t*)&pte)); __asm __volatile("mov psr.l=%0" :: "r" (psr)); __asm __volatile("srlz.i"); /* Expose the mapping to userland in ar.k5 */ ia64_set_k5(VM_MAX_ADDRESS); } static void calculate_frequencies(void) { struct ia64_sal_result sal; struct ia64_pal_result pal; sal = ia64_sal_entry(SAL_FREQ_BASE, 0, 0, 0, 0, 0, 0, 0); pal = ia64_call_pal_static(PAL_FREQ_RATIOS, 0, 0, 0); if (sal.sal_status == 0 && pal.pal_status == 0) { if (bootverbose) { printf("Platform clock frequency %ld Hz\n", sal.sal_result[0]); printf("Processor ratio %ld/%ld, Bus ratio %ld/%ld, " "ITC ratio %ld/%ld\n", pal.pal_result[0] >> 32, pal.pal_result[0] & ((1L << 32) - 1), pal.pal_result[1] >> 32, pal.pal_result[1] & ((1L << 32) - 1), pal.pal_result[2] >> 32, pal.pal_result[2] & ((1L << 32) - 1)); } processor_frequency = sal.sal_result[0] * (pal.pal_result[0] >> 32) / (pal.pal_result[0] & ((1L << 32) - 1)); bus_frequency = sal.sal_result[0] * (pal.pal_result[1] >> 32) / (pal.pal_result[1] & ((1L << 32) - 1)); itc_frequency = sal.sal_result[0] * (pal.pal_result[2] >> 32) / (pal.pal_result[2] & ((1L << 32) - 1)); } } void ia64_init(void) { int phys_avail_cnt; vm_offset_t kernstart, kernend; vm_offset_t kernstartpfn, kernendpfn, pfn0, pfn1; char *p; EFI_MEMORY_DESCRIPTOR *md, *mdp; int mdcount, i, metadata_missing; /* NO OUTPUT ALLOWED UNTIL FURTHER NOTICE */ /* * TODO: Disable interrupts, floating point etc. * Maybe flush cache and tlb */ ia64_set_fpsr(IA64_FPSR_DEFAULT); /* * TODO: Get critical system information (if possible, from the * information provided by the boot program). */ /* * pa_bootinfo is the physical address of the bootinfo block as * passed to us by the loader and set in locore.s. */ bootinfo = *(struct bootinfo *)(IA64_PHYS_TO_RR7(pa_bootinfo)); if (bootinfo.bi_magic != BOOTINFO_MAGIC || bootinfo.bi_version != 1) { bzero(&bootinfo, sizeof(bootinfo)); bootinfo.bi_kernend = (vm_offset_t) round_page(_end); } /* * Look for the I/O ports first - we need them for console * probing. */ mdcount = bootinfo.bi_memmap_size / bootinfo.bi_memdesc_size; md = (EFI_MEMORY_DESCRIPTOR *) IA64_PHYS_TO_RR7(bootinfo.bi_memmap); for (i = 0, mdp = md; i < mdcount; i++, mdp = NextMemoryDescriptor(mdp, bootinfo.bi_memdesc_size)) { if (mdp->Type == EfiMemoryMappedIOPortSpace) ia64_port_base = IA64_PHYS_TO_RR6(mdp->PhysicalStart); else if (mdp->Type == EfiPalCode) ia64_pal_base = mdp->PhysicalStart; } metadata_missing = 0; if (bootinfo.bi_modulep) preload_metadata = (caddr_t)bootinfo.bi_modulep; else metadata_missing = 1; if (envmode == 1) kern_envp = static_env; else kern_envp = (caddr_t)bootinfo.bi_envp; /* * Look at arguments passed to us and compute boothowto. */ boothowto = bootinfo.bi_boothowto; /* * Catch case of boot_verbose set in environment. */ if ((p = getenv("boot_verbose")) != NULL) { if (strcmp(p, "yes") == 0 || strcmp(p, "YES") == 0) { boothowto |= RB_VERBOSE; } freeenv(p); } if (boothowto & RB_VERBOSE) bootverbose = 1; /* * Initialize the console before we print anything out. */ cninit(); /* OUTPUT NOW ALLOWED */ if (ia64_pal_base != 0) { ia64_pal_base &= ~IA64_ID_PAGE_MASK; /* * We use a TR to map the first 256M of memory - this might * cover the palcode too. */ if (ia64_pal_base == 0) printf("PAL code mapped by the kernel's TR\n"); } else printf("PAL code not found\n"); /* * Wire things up so we can call the firmware. */ map_pal_code(); ia64_efi_init(); calculate_frequencies(); /* * Find the beginning and end of the kernel. */ kernstart = trunc_page(kernel_text); #ifdef DDB ksym_start = (void *)bootinfo.bi_symtab; ksym_end = (void *)bootinfo.bi_esymtab; kernend = (vm_offset_t)round_page(ksym_end); #else kernend = (vm_offset_t)round_page(_end); #endif /* But if the bootstrap tells us otherwise, believe it! */ if (bootinfo.bi_kernend) kernend = round_page(bootinfo.bi_kernend); if (metadata_missing) printf("WARNING: loader(8) metadata is missing!\n"); /* Get FPSWA interface */ fpswa_interface = (FPSWA_INTERFACE*)IA64_PHYS_TO_RR7(bootinfo.bi_fpswa); /* Init basic tunables, including hz */ init_param1(); p = getenv("kernelname"); if (p) { strncpy(kernelname, p, sizeof(kernelname) - 1); freeenv(p); } kernstartpfn = atop(IA64_RR_MASK(kernstart)); kernendpfn = atop(IA64_RR_MASK(kernend)); /* * Size the memory regions and load phys_avail[] with the results. */ /* * Find out how much memory is available, by looking at * the memory descriptors. */ #ifdef DEBUG_MD printf("Memory descriptor count: %d\n", mdcount); #endif phys_avail_cnt = 0; for (i = 0, mdp = md; i < mdcount; i++, mdp = NextMemoryDescriptor(mdp, bootinfo.bi_memdesc_size)) { #ifdef DEBUG_MD printf("MD %d: type %d pa 0x%lx cnt 0x%lx\n", i, mdp->Type, mdp->PhysicalStart, mdp->NumberOfPages); #endif pfn0 = ia64_btop(round_page(mdp->PhysicalStart)); pfn1 = ia64_btop(trunc_page(mdp->PhysicalStart + mdp->NumberOfPages * 4096)); if (pfn1 <= pfn0) continue; if (mdp->Type != EfiConventionalMemory) continue; /* * Wimp out for now since we do not DTRT here with * pci bus mastering (no bounce buffering, for example). */ if (pfn0 >= ia64_btop(0x100000000UL)) { printf("Skipping memory chunk start 0x%lx\n", mdp->PhysicalStart); continue; } if (pfn1 >= ia64_btop(0x100000000UL)) { printf("Skipping memory chunk end 0x%lx\n", mdp->PhysicalStart + mdp->NumberOfPages * 4096); continue; } /* * We have a memory descriptor that describes conventional * memory that is for general use. We must determine if the * loader has put the kernel in this region. */ physmem += (pfn1 - pfn0); if (pfn0 <= kernendpfn && kernstartpfn <= pfn1) { /* * Must compute the location of the kernel * within the segment. */ #ifdef DEBUG_MD printf("Descriptor %d contains kernel\n", i); #endif if (pfn0 < kernstartpfn) { /* * There is a chunk before the kernel. */ #ifdef DEBUG_MD printf("Loading chunk before kernel: " "0x%lx / 0x%lx\n", pfn0, kernstartpfn); #endif phys_avail[phys_avail_cnt] = ia64_ptob(pfn0); phys_avail[phys_avail_cnt+1] = ia64_ptob(kernstartpfn); phys_avail_cnt += 2; } if (kernendpfn < pfn1) { /* * There is a chunk after the kernel. */ #ifdef DEBUG_MD printf("Loading chunk after kernel: " "0x%lx / 0x%lx\n", kernendpfn, pfn1); #endif phys_avail[phys_avail_cnt] = ia64_ptob(kernendpfn); phys_avail[phys_avail_cnt+1] = ia64_ptob(pfn1); phys_avail_cnt += 2; } } else { /* * Just load this cluster as one chunk. */ #ifdef DEBUG_MD printf("Loading descriptor %d: 0x%lx / 0x%lx\n", i, pfn0, pfn1); #endif phys_avail[phys_avail_cnt] = ia64_ptob(pfn0); phys_avail[phys_avail_cnt+1] = ia64_ptob(pfn1); phys_avail_cnt += 2; } } phys_avail[phys_avail_cnt] = 0; Maxmem = physmem; init_param2(physmem); /* * Initialize error message buffer (at end of core). */ msgbufp = (struct msgbuf *)pmap_steal_memory(MSGBUF_SIZE); msgbufinit(msgbufp, MSGBUF_SIZE); proc_linkup(&proc0, &ksegrp0, &kse0, &thread0); /* * Init mapping for u page(s) for proc 0 */ proc0uarea = (struct user *)pmap_steal_memory(UAREA_PAGES * PAGE_SIZE); proc0kstack = (vm_offset_t)kstack; proc0.p_uarea = proc0uarea; thread0.td_kstack = proc0kstack; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; /* * Setup the global data for the bootstrap cpu. */ pcpup = (struct pcpu *)pmap_steal_memory(PAGE_SIZE); ia64_set_k4((u_int64_t)pcpup); pcpu_init(pcpup, 0, PAGE_SIZE); PCPU_SET(curthread, &thread0); /* * Initialize the rest of proc 0's PCB. * * Set the kernel sp, reserving space for an (empty) trapframe, * and make proc0's trapframe pointer point to it for sanity. * Initialise proc0's backing store to start after u area. */ thread0.td_frame = (struct trapframe *)thread0.td_pcb - 1; thread0.td_frame->tf_length = sizeof(struct trapframe); thread0.td_frame->tf_flags = FRAME_SYSCALL; thread0.td_pcb->pcb_special.sp = (u_int64_t)thread0.td_frame - 16; thread0.td_pcb->pcb_special.bspstore = (u_int64_t)proc0kstack; mutex_init(); /* * Initialize the virtual memory system. */ pmap_bootstrap(); /* * Initialize debuggers, and break into them if appropriate. */ #ifdef DDB kdb_init(); if (boothowto & RB_KDB) { printf("Boot flags requested debugger\n"); breakpoint(); } #endif ia64_set_tpr(0); /* * Save our current context so that we have a known (maybe even * sane) context as the initial context for new threads that are * forked from us. If any of those threads (including thread0) * does something wrong, we may be lucky and return here where * we're ready for them with a nice panic. */ if (!savectx(thread0.td_pcb)) mi_startup(); /* We should not get here. */ panic("ia64_init: Whooaa there!"); /* NOTREACHED */ } void bzero(void *buf, size_t len) { caddr_t p = buf; while (((vm_offset_t) p & (sizeof(u_long) - 1)) && len) { *p++ = 0; len--; } while (len >= sizeof(u_long) * 8) { *(u_long*) p = 0; *((u_long*) p + 1) = 0; *((u_long*) p + 2) = 0; *((u_long*) p + 3) = 0; len -= sizeof(u_long) * 8; *((u_long*) p + 4) = 0; *((u_long*) p + 5) = 0; *((u_long*) p + 6) = 0; *((u_long*) p + 7) = 0; p += sizeof(u_long) * 8; } while (len >= sizeof(u_long)) { *(u_long*) p = 0; len -= sizeof(u_long); p += sizeof(u_long); } while (len) { *p++ = 0; len--; } } void DELAY(int n) { u_int64_t start, end, now; start = ia64_get_itc(); end = start + (itc_frequency * n) / 1000000; /* printf("DELAY from 0x%lx to 0x%lx\n", start, end); */ do { now = ia64_get_itc(); } while (now < end || (now > start && end < start)); } /* * Send an interrupt (signal) to a process. */ void sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code) { struct proc *p; struct thread *td; struct trapframe *tf; struct sigacts *psp; struct sigframe sf, *sfp; u_int64_t sbs, sp; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); tf = td->td_frame; sp = tf->tf_special.sp; oonstack = sigonstack(sp); sbs = 0; /* save user context */ bzero(&sf, sizeof(struct sigframe)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; /* * Allocate and validate space for the signal handler * context. Note that if the stack is in P0 space, the * call to grow() is a nop, and the useracc() check * will fail if the process has not already allocated * the space with a `brk'. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sbs = (u_int64_t)td->td_sigstk.ss_sp; sbs = (sbs + 15) & ~15; sfp = (struct sigframe *)(sbs + td->td_sigstk.ss_size); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe *)sp; sfp = (struct sigframe *)((u_int64_t)(sfp - 1) & ~15); /* Fill in the siginfo structure for POSIX handlers. */ if (SIGISMEMBER(psp->ps_siginfo, sig)) { sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void*)tf->tf_special.ifa; code = (u_int64_t)&sfp->sf_si; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); get_mcontext(td, &sf.sf_uc.uc_mcontext, 0); /* Copy the frame out to userland. */ if (copyout(&sf, sfp, sizeof(sf)) != 0) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); sigexit(td, SIGILL); return; } if ((tf->tf_flags & FRAME_SYSCALL) == 0) { tf->tf_special.psr &= ~IA64_PSR_RI; tf->tf_special.iip = ia64_get_k5() + ((uint64_t)break_sigtramp - (uint64_t)ia64_gateway_page); } else tf->tf_special.iip = ia64_get_k5() + ((uint64_t)epc_sigtramp - (uint64_t)ia64_gateway_page); /* * Setup the trapframe to return to the signal trampoline. We pass * information to the trampoline in the following registers: * * gp new backing store or NULL * r8 signal number * r9 signal code or siginfo pointer * r10 signal handler (function descriptor) */ tf->tf_special.sp = (u_int64_t)sfp - 16; tf->tf_special.gp = sbs; tf->tf_special.bspstore = sf.sf_uc.uc_mcontext.mc_special.bspstore; tf->tf_special.ndirty = 0; tf->tf_special.rnat = sf.sf_uc.uc_mcontext.mc_special.rnat; tf->tf_scratch.gr8 = sig; tf->tf_scratch.gr9 = code; tf->tf_scratch.gr10 = (u_int64_t)catcher; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } /* * Build siginfo_t for SA thread */ void cpu_thread_siginfo(int sig, u_long code, siginfo_t *si) { struct proc *p; struct thread *td; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); bzero(si, sizeof(*si)); si->si_signo = sig; si->si_code = code; /* XXXKSE fill other fields */ } /* * 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 * state to gain improper privileges. * * MPSAFE */ int sigreturn(struct thread *td, struct sigreturn_args /* { ucontext_t *sigcntxp; } */ *uap) { ucontext_t uc; struct trapframe *tf; struct proc *p; struct pcb *pcb; tf = td->td_frame; p = td->td_proc; pcb = td->td_pcb; /* * Fetch the entire context structure at once for speed. * We don't use a normal argument to simplify RSE handling. */ if (copyin(uap->sigcntxp, (caddr_t)&uc, sizeof(uc))) return (EFAULT); set_mcontext(td, &uc.uc_mcontext); PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (sigonstack(tf->tf_special.sp)) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = uc.uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } #ifdef COMPAT_FREEBSD4 int freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args *uap) { return sigreturn(td, (struct sigreturn_args *)uap); } #endif int get_mcontext(struct thread *td, mcontext_t *mc, int flags) { struct trapframe *tf; uint64_t bspst, kstk, rnat; tf = td->td_frame; bzero(mc, sizeof(*mc)); if (tf->tf_special.ndirty != 0) { kstk = td->td_kstack + (tf->tf_special.bspstore & 0x1ffUL); __asm __volatile("mov ar.rsc=0;;"); __asm __volatile("mov %0=ar.bspstore" : "=r"(bspst)); /* Make sure we have all the user registers written out. */ if (bspst - kstk < tf->tf_special.ndirty) { __asm __volatile("flushrs;;"); __asm __volatile("mov %0=ar.bspstore" : "=r"(bspst)); } __asm __volatile("mov %0=ar.rnat;;" : "=r"(rnat)); __asm __volatile("mov ar.rsc=3"); copyout((void*)kstk, (void*)tf->tf_special.bspstore, tf->tf_special.ndirty); kstk += tf->tf_special.ndirty; mc->mc_special = tf->tf_special; mc->mc_special.rnat = (bspst > kstk && (bspst & 0x1ffUL) < (kstk & 0x1ffUL)) ? *(uint64_t*)(kstk | 0x1f8UL) : rnat; mc->mc_special.bspstore += mc->mc_special.ndirty; mc->mc_special.ndirty = 0; } else mc->mc_special = tf->tf_special; if (tf->tf_flags & FRAME_SYSCALL) { mc->mc_flags |= _MC_FLAGS_SYSCALL_CONTEXT; mc->mc_scratch = tf->tf_scratch; if (flags & GET_MC_CLEAR_RET) { mc->mc_scratch.gr8 = 0; mc->mc_scratch.gr9 = 0; mc->mc_scratch.gr10 = 0; mc->mc_scratch.gr11 = 0; } } else { mc->mc_flags |= _MC_FLAGS_ASYNC_CONTEXT; mc->mc_scratch = tf->tf_scratch; mc->mc_scratch_fp = tf->tf_scratch_fp; /* * XXX If the thread never used the high FP registers, we * probably shouldn't waste time saving them. */ ia64_highfp_save(td); mc->mc_flags |= _MC_FLAGS_HIGHFP_VALID; mc->mc_high_fp = td->td_pcb->pcb_high_fp; } save_callee_saved(&mc->mc_preserved); save_callee_saved_fp(&mc->mc_preserved_fp); return (0); } int set_mcontext(struct thread *td, const mcontext_t *mc) { struct _special s; struct trapframe *tf; uint64_t psrmask; tf = td->td_frame; KASSERT((tf->tf_special.ndirty & ~PAGE_MASK) == 0, ("Whoa there! We have more than 8KB of dirty registers!")); s = mc->mc_special; /* * Only copy the user mask and the restart instruction bit from * the new context. */ psrmask = IA64_PSR_BE | IA64_PSR_UP | IA64_PSR_AC | IA64_PSR_MFL | IA64_PSR_MFH | IA64_PSR_RI; s.psr = (tf->tf_special.psr & ~psrmask) | (s.psr & psrmask); /* We don't have any dirty registers of the new context. */ s.ndirty = 0; if (mc->mc_flags & _MC_FLAGS_ASYNC_CONTEXT) { /* * We can get an async context passed to us while we * entered the kernel through a syscall: sigreturn(2) * and kse_switchin(2) both take contexts that could * previously be the result of a trap or interrupt. * Hence, we cannot assert that the trapframe is not * a syscall frame, but we can assert that it's at * least an expected syscall. */ if (tf->tf_flags & FRAME_SYSCALL) { KASSERT(tf->tf_scratch.gr15 == SYS_sigreturn || tf->tf_scratch.gr15 == SYS_kse_switchin, ("foo")); tf->tf_flags &= ~FRAME_SYSCALL; } tf->tf_scratch = mc->mc_scratch; tf->tf_scratch_fp = mc->mc_scratch_fp; if (mc->mc_flags & _MC_FLAGS_HIGHFP_VALID) td->td_pcb->pcb_high_fp = mc->mc_high_fp; } else { KASSERT((tf->tf_flags & FRAME_SYSCALL) != 0, ("foo")); if ((mc->mc_flags & _MC_FLAGS_SYSCALL_CONTEXT) == 0) { s.cfm = tf->tf_special.cfm; s.iip = tf->tf_special.iip; tf->tf_scratch.gr15 = 0; /* Clear syscall nr. */ } else tf->tf_scratch = mc->mc_scratch; } tf->tf_special = s; restore_callee_saved(&mc->mc_preserved); restore_callee_saved_fp(&mc->mc_preserved_fp); if (mc->mc_flags & _MC_FLAGS_KSE_SET_MBOX) suword((caddr_t)mc->mc_special.ifa, mc->mc_special.isr); return (0); } /* * Clear registers on exec. */ void exec_setregs(struct thread *td, u_long entry, u_long stack, u_long ps_strings) { struct trapframe *tf; uint64_t *ksttop, *kst; tf = td->td_frame; ksttop = (uint64_t*)(td->td_kstack + tf->tf_special.ndirty + (tf->tf_special.bspstore & 0x1ffUL)); /* * We can ignore up to 8KB of dirty registers by masking off the * lower 13 bits in exception_restore() or epc_syscall(). This * should be enough for a couple of years, but if there are more * than 8KB of dirty registers, we lose track of the bottom of * the kernel stack. The solution is to copy the active part of * the kernel stack down 1 page (or 2, but not more than that) * so that we always have less than 8KB of dirty registers. */ KASSERT((tf->tf_special.ndirty & ~PAGE_MASK) == 0, ("Whoa there! We have more than 8KB of dirty registers!")); bzero(&tf->tf_special, sizeof(tf->tf_special)); if ((tf->tf_flags & FRAME_SYSCALL) == 0) { /* break syscalls. */ bzero(&tf->tf_scratch, sizeof(tf->tf_scratch)); bzero(&tf->tf_scratch_fp, sizeof(tf->tf_scratch_fp)); tf->tf_special.cfm = (1UL<<63) | (3UL<<7) | 3UL; tf->tf_special.bspstore = IA64_BACKINGSTORE; /* * Copy the arguments onto the kernel register stack so that * they get loaded by the loadrs instruction. Skip over the * NaT collection points. */ kst = ksttop - 1; if (((uintptr_t)kst & 0x1ff) == 0x1f8) *kst-- = 0; *kst-- = 0; if (((uintptr_t)kst & 0x1ff) == 0x1f8) *kst-- = 0; *kst-- = ps_strings; if (((uintptr_t)kst & 0x1ff) == 0x1f8) *kst-- = 0; *kst = stack; tf->tf_special.ndirty = (ksttop - kst) << 3; } else { /* epc syscalls (default). */ tf->tf_special.cfm = (3UL<<62) | (3UL<<7) | 3UL; tf->tf_special.bspstore = IA64_BACKINGSTORE + 24; /* * Write values for out0, out1 and out2 to the user's backing * store and arrange for them to be restored into the user's * initial register frame. * Assumes that (bspstore & 0x1f8) < 0x1e0. */ suword((caddr_t)tf->tf_special.bspstore - 24, stack); suword((caddr_t)tf->tf_special.bspstore - 16, ps_strings); suword((caddr_t)tf->tf_special.bspstore - 8, 0); } tf->tf_special.iip = entry; tf->tf_special.sp = (stack & ~15) - 16; tf->tf_special.rsc = 0xf; tf->tf_special.fpsr = IA64_FPSR_DEFAULT; tf->tf_special.psr = IA64_PSR_IC | IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_RT | IA64_PSR_DFH | IA64_PSR_BN | IA64_PSR_CPL_USER; } int ptrace_set_pc(struct thread *td, unsigned long addr) { uint64_t slot; switch (addr & 0xFUL) { case 0: slot = IA64_PSR_RI_0; break; case 1: /* XXX we need to deal with MLX bundles here */ slot = IA64_PSR_RI_1; break; case 2: slot = IA64_PSR_RI_2; break; default: return (EINVAL); } td->td_frame->tf_special.iip = addr & ~0x0FULL; td->td_frame->tf_special.psr = (td->td_frame->tf_special.psr & ~IA64_PSR_RI) | slot; return (0); } int ptrace_single_step(struct thread *td) { td->td_frame->tf_special.psr |= IA64_PSR_SS; return (0); } int fill_regs(struct thread *td, struct reg *regs) { struct trapframe *tf; tf = td->td_frame; regs->r_special = tf->tf_special; regs->r_scratch = tf->tf_scratch; save_callee_saved(®s->r_preserved); return (0); } int set_regs(struct thread *td, struct reg *regs) { struct trapframe *tf; tf = td->td_frame; tf->tf_special = regs->r_special; tf->tf_scratch = regs->r_scratch; restore_callee_saved(®s->r_preserved); return (0); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { return (ENOSYS); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { return (ENOSYS); } int fill_fpregs(struct thread *td, struct fpreg *fpregs) { struct trapframe *frame = td->td_frame; struct pcb *pcb = td->td_pcb; /* Save the high FP registers. */ ia64_highfp_save(td); fpregs->fpr_scratch = frame->tf_scratch_fp; save_callee_saved_fp(&fpregs->fpr_preserved); fpregs->fpr_high = pcb->pcb_high_fp; return (0); } int set_fpregs(struct thread *td, struct fpreg *fpregs) { struct trapframe *frame = td->td_frame; struct pcb *pcb = td->td_pcb; /* Throw away the high FP registers (should be redundant). */ ia64_highfp_drop(td); frame->tf_scratch_fp = fpregs->fpr_scratch; restore_callee_saved_fp(&fpregs->fpr_preserved); pcb->pcb_high_fp = fpregs->fpr_high; return (0); } /* * High FP register functions. * XXX no synchronization yet. */ int ia64_highfp_drop(struct thread *td) { struct pcb *pcb; struct pcpu *cpu; struct thread *thr; pcb = td->td_pcb; cpu = pcb->pcb_fpcpu; if (cpu == NULL) return (0); pcb->pcb_fpcpu = NULL; thr = cpu->pc_fpcurthread; cpu->pc_fpcurthread = NULL; /* Post-mortem sanity checking. */ KASSERT(thr == td, ("Inconsistent high FP state")); return (1); } int ia64_highfp_save(struct thread *td) { struct pcb *pcb; struct pcpu *cpu; struct thread *thr; /* Don't save if the high FP registers weren't modified. */ if ((td->td_frame->tf_special.psr & IA64_PSR_MFH) == 0) return (ia64_highfp_drop(td)); pcb = td->td_pcb; cpu = pcb->pcb_fpcpu; if (cpu == NULL) return (0); #ifdef SMP if (cpu != pcpup) { ipi_send(cpu->pc_lid, IPI_HIGH_FP); while (pcb->pcb_fpcpu != cpu) DELAY(100); return (1); } #endif save_high_fp(&pcb->pcb_high_fp); pcb->pcb_fpcpu = NULL; thr = cpu->pc_fpcurthread; cpu->pc_fpcurthread = NULL; /* Post-mortem sanity cxhecking. */ KASSERT(thr == td, ("Inconsistent high FP state")); return (1); } #ifndef DDB void Debugger(const char *msg) { printf("Debugger(\"%s\") called.\n", msg); } #endif /* no DDB */ int sysbeep(int pitch, int period) { return (ENODEV); } Index: head/sys/kern/kern_descrip.c =================================================================== --- head/sys/kern/kern_descrip.c (revision 130343) +++ head/sys/kern/kern_descrip.c (revision 130344) @@ -1,2500 +1,2500 @@ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 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. * * @(#)kern_descrip.c 8.6 (Berkeley) 4/19/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_FILEDESC, "file desc", "Open file descriptor table"); static MALLOC_DEFINE(M_FILEDESC_TO_LEADER, "file desc to leader", "file desc to leader structures"); static MALLOC_DEFINE(M_SIGIO, "sigio", "sigio structures"); static uma_zone_t file_zone; static d_open_t fdopen; #define NUMFDESC 64 #define CDEV_MAJOR 22 static struct cdevsw fildesc_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = fdopen, .d_name = "FD", .d_maj = CDEV_MAJOR, }; /* How to treat 'new' parameter when allocating a fd for do_dup(). */ enum dup_type { DUP_VARIABLE, DUP_FIXED }; static int do_dup(struct thread *td, enum dup_type type, int old, int new, register_t *retval); static int fd_first_free(struct filedesc *, int, int); static int fd_last_used(struct filedesc *, int, int); static void fdgrowtable(struct filedesc *, int); /* * Descriptor management. */ struct filelist filehead; /* head of list of open files */ int nfiles; /* actual number of open files */ struct sx filelist_lock; /* sx to protect filelist */ struct mtx sigio_lock; /* mtx to protect pointers to sigio */ /* * Find the first zero bit in the given bitmap, starting at low and not * exceeding size - 1. */ static int fd_first_free(struct filedesc *fdp, int low, int size) { NDSLOTTYPE *map = fdp->fd_map; NDSLOTTYPE mask; int off, maxoff; if (low >= size) return (low); off = NDSLOT(low); if (low % NDENTRIES) { mask = ~(~(NDSLOTTYPE)0 >> (NDENTRIES - (low % NDENTRIES))); if ((mask &= ~map[off]) != 0UL) return (off * NDENTRIES + ffsl(mask) - 1); ++off; } for (maxoff = NDSLOTS(size); off < maxoff; ++off) if (map[off] != ~0UL) return (off * NDENTRIES + ffsl(~map[off]) - 1); return (size); } /* * Find the highest non-zero bit in the given bitmap, starting at low and * not exceeding size - 1. */ static int fd_last_used(struct filedesc *fdp, int low, int size) { NDSLOTTYPE *map = fdp->fd_map; NDSLOTTYPE mask; int off, minoff; if (low >= size) return (-1); off = NDSLOT(size); if (size % NDENTRIES) { mask = ~(~(NDSLOTTYPE)0 << (size % NDENTRIES)); if ((mask &= map[off]) != 0) return (off * NDENTRIES + flsl(mask) - 1); --off; } for (minoff = NDSLOT(low); off >= minoff; --off) if (map[off] != 0) return (off * NDENTRIES + flsl(map[off]) - 1); return (size - 1); } static int fdisused(struct filedesc *fdp, int fd) { KASSERT(fd >= 0 && fd < fdp->fd_nfiles, ("file descriptor %d out of range (0, %d)", fd, fdp->fd_nfiles)); return ((fdp->fd_map[NDSLOT(fd)] & NDBIT(fd)) != 0); } /* * Mark a file descriptor as used. */ void fdused(struct filedesc *fdp, int fd) { FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); KASSERT(!fdisused(fdp, fd), ("fd already used")); fdp->fd_map[NDSLOT(fd)] |= NDBIT(fd); if (fd > fdp->fd_lastfile) fdp->fd_lastfile = fd; if (fd == fdp->fd_freefile) fdp->fd_freefile = fd_first_free(fdp, fd, fdp->fd_nfiles); } /* * Mark a file descriptor as unused. */ void fdunused(struct filedesc *fdp, int fd) { FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); KASSERT(fdisused(fdp, fd), ("fd is already unused")); KASSERT(fdp->fd_ofiles[fd] == NULL, ("fd is still in use")); fdp->fd_map[NDSLOT(fd)] &= ~NDBIT(fd); if (fd < fdp->fd_freefile) fdp->fd_freefile = fd; if (fd == fdp->fd_lastfile) fdp->fd_lastfile = fd_last_used(fdp, 0, fd); } /* * System calls on descriptors. */ #ifndef _SYS_SYSPROTO_H_ struct getdtablesize_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getdtablesize(td, uap) struct thread *td; struct getdtablesize_args *uap; { struct proc *p = td->td_proc; PROC_LOCK(p); td->td_retval[0] = min((int)lim_cur(p, RLIMIT_NOFILE), maxfilesperproc); PROC_UNLOCK(p); return (0); } /* * Duplicate a file descriptor to a particular value. * * note: keep in mind that a potential race condition exists when closing * descriptors from a shared descriptor table (via rfork). */ #ifndef _SYS_SYSPROTO_H_ struct dup2_args { u_int from; u_int to; }; #endif /* * MPSAFE */ /* ARGSUSED */ int dup2(td, uap) struct thread *td; struct dup2_args *uap; { return (do_dup(td, DUP_FIXED, (int)uap->from, (int)uap->to, td->td_retval)); } /* * Duplicate a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct dup_args { u_int fd; }; #endif /* * MPSAFE */ /* ARGSUSED */ int dup(td, uap) struct thread *td; struct dup_args *uap; { return (do_dup(td, DUP_VARIABLE, (int)uap->fd, 0, td->td_retval)); } /* * The file control system call. */ #ifndef _SYS_SYSPROTO_H_ struct fcntl_args { int fd; int cmd; long arg; }; #endif /* * MPSAFE */ /* ARGSUSED */ int fcntl(td, uap) struct thread *td; struct fcntl_args *uap; { struct flock fl; intptr_t arg; int error; error = 0; switch (uap->cmd) { case F_GETLK: case F_SETLK: case F_SETLKW: error = copyin((void *)(intptr_t)uap->arg, &fl, sizeof(fl)); arg = (intptr_t)&fl; break; default: arg = uap->arg; break; } if (error) return (error); error = kern_fcntl(td, uap->fd, uap->cmd, arg); if (error) return (error); if (uap->cmd == F_GETLK) error = copyout(&fl, (void *)(intptr_t)uap->arg, sizeof(fl)); return (error); } int kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) { struct filedesc *fdp; struct flock *flp; struct file *fp; struct proc *p; char *pop; struct vnode *vp; u_int newmin; int error, flg, tmp; error = 0; flg = F_POSIX; p = td->td_proc; fdp = p->p_fd; mtx_lock(&Giant); FILEDESC_LOCK(fdp); if ((unsigned)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) { FILEDESC_UNLOCK(fdp); error = EBADF; goto done2; } pop = &fdp->fd_ofileflags[fd]; switch (cmd) { case F_DUPFD: FILEDESC_UNLOCK(fdp); newmin = arg; PROC_LOCK(p); if (newmin >= lim_cur(p, RLIMIT_NOFILE) || newmin >= maxfilesperproc) { PROC_UNLOCK(p); error = EINVAL; break; } PROC_UNLOCK(p); error = do_dup(td, DUP_VARIABLE, fd, newmin, td->td_retval); break; case F_GETFD: td->td_retval[0] = (*pop & UF_EXCLOSE) ? FD_CLOEXEC : 0; FILEDESC_UNLOCK(fdp); break; case F_SETFD: *pop = (*pop &~ UF_EXCLOSE) | (arg & FD_CLOEXEC ? UF_EXCLOSE : 0); FILEDESC_UNLOCK(fdp); break; case F_GETFL: FILE_LOCK(fp); FILEDESC_UNLOCK(fdp); td->td_retval[0] = OFLAGS(fp->f_flag); FILE_UNLOCK(fp); break; case F_SETFL: FILE_LOCK(fp); FILEDESC_UNLOCK(fdp); fhold_locked(fp); fp->f_flag &= ~FCNTLFLAGS; fp->f_flag |= FFLAGS(arg & ~O_ACCMODE) & FCNTLFLAGS; FILE_UNLOCK(fp); tmp = fp->f_flag & FNONBLOCK; error = fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td); if (error) { fdrop(fp, td); break; } tmp = fp->f_flag & FASYNC; error = fo_ioctl(fp, FIOASYNC, &tmp, td->td_ucred, td); if (error == 0) { fdrop(fp, td); break; } FILE_LOCK(fp); fp->f_flag &= ~FNONBLOCK; FILE_UNLOCK(fp); tmp = 0; (void)fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td); fdrop(fp, td); break; case F_GETOWN: fhold(fp); FILEDESC_UNLOCK(fdp); error = fo_ioctl(fp, FIOGETOWN, &tmp, td->td_ucred, td); if (error == 0) td->td_retval[0] = tmp; fdrop(fp, td); break; case F_SETOWN: fhold(fp); FILEDESC_UNLOCK(fdp); tmp = arg; error = fo_ioctl(fp, FIOSETOWN, &tmp, td->td_ucred, td); fdrop(fp, td); break; case F_SETLKW: flg |= F_WAIT; /* FALLTHROUGH F_SETLK */ case F_SETLK: if (fp->f_type != DTYPE_VNODE) { FILEDESC_UNLOCK(fdp); error = EBADF; break; } flp = (struct flock *)arg; if (flp->l_whence == SEEK_CUR) { if (fp->f_offset < 0 || (flp->l_start > 0 && fp->f_offset > OFF_MAX - flp->l_start)) { FILEDESC_UNLOCK(fdp); error = EOVERFLOW; break; } flp->l_start += fp->f_offset; } /* * VOP_ADVLOCK() may block. */ fhold(fp); FILEDESC_UNLOCK(fdp); vp = fp->f_vnode; switch (flp->l_type) { case F_RDLCK: if ((fp->f_flag & FREAD) == 0) { error = EBADF; break; } PROC_LOCK(p->p_leader); p->p_leader->p_flag |= P_ADVLOCK; PROC_UNLOCK(p->p_leader); error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_SETLK, flp, flg); break; case F_WRLCK: if ((fp->f_flag & FWRITE) == 0) { error = EBADF; break; } PROC_LOCK(p->p_leader); p->p_leader->p_flag |= P_ADVLOCK; PROC_UNLOCK(p->p_leader); error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_SETLK, flp, flg); break; case F_UNLCK: error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCK, flp, F_POSIX); break; default: error = EINVAL; break; } /* Check for race with close */ FILEDESC_LOCK(fdp); if ((unsigned) fd >= fdp->fd_nfiles || fp != fdp->fd_ofiles[fd]) { FILEDESC_UNLOCK(fdp); flp->l_whence = SEEK_SET; flp->l_start = 0; flp->l_len = 0; flp->l_type = F_UNLCK; (void) VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCK, flp, F_POSIX); } else FILEDESC_UNLOCK(fdp); fdrop(fp, td); break; case F_GETLK: if (fp->f_type != DTYPE_VNODE) { FILEDESC_UNLOCK(fdp); error = EBADF; break; } flp = (struct flock *)arg; if (flp->l_type != F_RDLCK && flp->l_type != F_WRLCK && flp->l_type != F_UNLCK) { FILEDESC_UNLOCK(fdp); error = EINVAL; break; } if (flp->l_whence == SEEK_CUR) { if ((flp->l_start > 0 && fp->f_offset > OFF_MAX - flp->l_start) || (flp->l_start < 0 && fp->f_offset < OFF_MIN - flp->l_start)) { FILEDESC_UNLOCK(fdp); error = EOVERFLOW; break; } flp->l_start += fp->f_offset; } /* * VOP_ADVLOCK() may block. */ fhold(fp); FILEDESC_UNLOCK(fdp); vp = fp->f_vnode; error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_GETLK, flp, F_POSIX); fdrop(fp, td); break; default: FILEDESC_UNLOCK(fdp); error = EINVAL; break; } done2: mtx_unlock(&Giant); return (error); } /* * Common code for dup, dup2, and fcntl(F_DUPFD). */ static int do_dup(td, type, old, new, retval) enum dup_type type; int old, new; register_t *retval; struct thread *td; { struct filedesc *fdp; struct proc *p; struct file *fp; struct file *delfp; int error, holdleaders, maxfd; KASSERT((type == DUP_VARIABLE || type == DUP_FIXED), ("invalid dup type %d", type)); p = td->td_proc; fdp = p->p_fd; /* * Verify we have a valid descriptor to dup from and possibly to * dup to. */ if (old < 0 || new < 0) return (EBADF); PROC_LOCK(p); maxfd = min((int)lim_cur(p, RLIMIT_NOFILE), maxfilesperproc); PROC_UNLOCK(p); if (new >= maxfd) return (EMFILE); FILEDESC_LOCK(fdp); if (old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL) { FILEDESC_UNLOCK(fdp); return (EBADF); } if (type == DUP_FIXED && old == new) { *retval = new; FILEDESC_UNLOCK(fdp); return (0); } fp = fdp->fd_ofiles[old]; fhold(fp); /* * If the caller specified a file descriptor, make sure the file * table is large enough to hold it, and grab it. Otherwise, just * allocate a new descriptor the usual way. Since the filedesc * lock may be temporarily dropped in the process, we have to look * out for a race. */ if (type == DUP_FIXED) { if (new >= fdp->fd_nfiles) fdgrowtable(fdp, new + 1); if (fdp->fd_ofiles[new] == NULL) fdused(fdp, new); } else { if ((error = fdalloc(td, new, &new)) != 0) { FILEDESC_UNLOCK(fdp); fdrop(fp, td); return (error); } } /* * If the old file changed out from under us then treat it as a * bad file descriptor. Userland should do its own locking to * avoid this case. */ if (fdp->fd_ofiles[old] != fp) { /* we've allocated a descriptor which we won't use */ if (fdp->fd_ofiles[new] == NULL) fdunused(fdp, new); FILEDESC_UNLOCK(fdp); fdrop(fp, td); return (EBADF); } KASSERT(old != new, ("new fd is same as old")); /* * Save info on the descriptor being overwritten. We cannot close * it without introducing an ownership race for the slot, since we * need to drop the filedesc lock to call closef(). * * XXX this duplicates parts of close(). */ delfp = fdp->fd_ofiles[new]; holdleaders = 0; if (delfp != NULL) { if (td->td_proc->p_fdtol != NULL) { /* * Ask fdfree() to sleep to ensure that all relevant * process leaders can be traversed in closef(). */ fdp->fd_holdleaderscount++; holdleaders = 1; } } /* * Duplicate the source descriptor */ fdp->fd_ofiles[new] = fp; fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] &~ UF_EXCLOSE; if (new > fdp->fd_lastfile) fdp->fd_lastfile = new; FILEDESC_UNLOCK(fdp); *retval = new; /* * If we dup'd over a valid file, we now own the reference to it * and must dispose of it using closef() semantics (as if a * close() were performed on it). * * XXX this duplicates parts of close(). */ if (delfp != NULL) { /* XXX need to call knote_fdclose() */ mtx_lock(&Giant); (void) closef(delfp, td); mtx_unlock(&Giant); if (holdleaders) { FILEDESC_LOCK(fdp); fdp->fd_holdleaderscount--; if (fdp->fd_holdleaderscount == 0 && fdp->fd_holdleaderswakeup != 0) { fdp->fd_holdleaderswakeup = 0; wakeup(&fdp->fd_holdleaderscount); } FILEDESC_UNLOCK(fdp); } } return (0); } /* * If sigio is on the list associated with a process or process group, * disable signalling from the device, remove sigio from the list and * free sigio. */ void funsetown(sigiop) struct sigio **sigiop; { struct sigio *sigio; SIGIO_LOCK(); sigio = *sigiop; if (sigio == NULL) { SIGIO_UNLOCK(); return; } *(sigio->sio_myref) = NULL; if ((sigio)->sio_pgid < 0) { struct pgrp *pg = (sigio)->sio_pgrp; PGRP_LOCK(pg); SLIST_REMOVE(&sigio->sio_pgrp->pg_sigiolst, sigio, sigio, sio_pgsigio); PGRP_UNLOCK(pg); } else { struct proc *p = (sigio)->sio_proc; PROC_LOCK(p); SLIST_REMOVE(&sigio->sio_proc->p_sigiolst, sigio, sigio, sio_pgsigio); PROC_UNLOCK(p); } SIGIO_UNLOCK(); crfree(sigio->sio_ucred); FREE(sigio, M_SIGIO); } /* * Free a list of sigio structures. * We only need to lock the SIGIO_LOCK because we have made ourselves * inaccessable to callers of fsetown and therefore do not need to lock * the proc or pgrp struct for the list manipulation. */ void funsetownlst(sigiolst) struct sigiolst *sigiolst; { struct proc *p; struct pgrp *pg; struct sigio *sigio; sigio = SLIST_FIRST(sigiolst); if (sigio == NULL) return; p = NULL; pg = NULL; /* * Every entry of the list should belong * to a single proc or pgrp. */ if (sigio->sio_pgid < 0) { pg = sigio->sio_pgrp; PGRP_LOCK_ASSERT(pg, MA_NOTOWNED); } else /* if (sigio->sio_pgid > 0) */ { p = sigio->sio_proc; PROC_LOCK_ASSERT(p, MA_NOTOWNED); } SIGIO_LOCK(); while ((sigio = SLIST_FIRST(sigiolst)) != NULL) { *(sigio->sio_myref) = NULL; if (pg != NULL) { KASSERT(sigio->sio_pgid < 0, ("Proc sigio in pgrp sigio list")); KASSERT(sigio->sio_pgrp == pg, ("Bogus pgrp in sigio list")); PGRP_LOCK(pg); SLIST_REMOVE(&pg->pg_sigiolst, sigio, sigio, sio_pgsigio); PGRP_UNLOCK(pg); } else /* if (p != NULL) */ { KASSERT(sigio->sio_pgid > 0, ("Pgrp sigio in proc sigio list")); KASSERT(sigio->sio_proc == p, ("Bogus proc in sigio list")); PROC_LOCK(p); SLIST_REMOVE(&p->p_sigiolst, sigio, sigio, sio_pgsigio); PROC_UNLOCK(p); } SIGIO_UNLOCK(); crfree(sigio->sio_ucred); FREE(sigio, M_SIGIO); SIGIO_LOCK(); } SIGIO_UNLOCK(); } /* * This is common code for FIOSETOWN ioctl called by fcntl(fd, F_SETOWN, arg). * * After permission checking, add a sigio structure to the sigio list for * the process or process group. */ int fsetown(pgid, sigiop) pid_t pgid; struct sigio **sigiop; { struct proc *proc; struct pgrp *pgrp; struct sigio *sigio; int ret; if (pgid == 0) { funsetown(sigiop); return (0); } ret = 0; /* Allocate and fill in the new sigio out of locks. */ MALLOC(sigio, struct sigio *, sizeof(struct sigio), M_SIGIO, M_WAITOK); sigio->sio_pgid = pgid; sigio->sio_ucred = crhold(curthread->td_ucred); sigio->sio_myref = sigiop; sx_slock(&proctree_lock); if (pgid > 0) { proc = pfind(pgid); if (proc == NULL) { ret = ESRCH; goto fail; } /* * Policy - Don't allow a process to FSETOWN a process * in another session. * * Remove this test to allow maximum flexibility or * restrict FSETOWN to the current process or process * group for maximum safety. */ PROC_UNLOCK(proc); if (proc->p_session != curthread->td_proc->p_session) { ret = EPERM; goto fail; } pgrp = NULL; } else /* if (pgid < 0) */ { pgrp = pgfind(-pgid); if (pgrp == NULL) { ret = ESRCH; goto fail; } PGRP_UNLOCK(pgrp); /* * Policy - Don't allow a process to FSETOWN a process * in another session. * * Remove this test to allow maximum flexibility or * restrict FSETOWN to the current process or process * group for maximum safety. */ if (pgrp->pg_session != curthread->td_proc->p_session) { ret = EPERM; goto fail; } proc = NULL; } funsetown(sigiop); if (pgid > 0) { PROC_LOCK(proc); /* * Since funsetownlst() is called without the proctree * locked, we need to check for P_WEXIT. * XXX: is ESRCH correct? */ if ((proc->p_flag & P_WEXIT) != 0) { PROC_UNLOCK(proc); ret = ESRCH; goto fail; } SLIST_INSERT_HEAD(&proc->p_sigiolst, sigio, sio_pgsigio); sigio->sio_proc = proc; PROC_UNLOCK(proc); } else { PGRP_LOCK(pgrp); SLIST_INSERT_HEAD(&pgrp->pg_sigiolst, sigio, sio_pgsigio); sigio->sio_pgrp = pgrp; PGRP_UNLOCK(pgrp); } sx_sunlock(&proctree_lock); SIGIO_LOCK(); *sigiop = sigio; SIGIO_UNLOCK(); return (0); fail: sx_sunlock(&proctree_lock); crfree(sigio->sio_ucred); FREE(sigio, M_SIGIO); return (ret); } /* * This is common code for FIOGETOWN ioctl called by fcntl(fd, F_GETOWN, arg). */ pid_t fgetown(sigiop) struct sigio **sigiop; { pid_t pgid; SIGIO_LOCK(); pgid = (*sigiop != NULL) ? (*sigiop)->sio_pgid : 0; SIGIO_UNLOCK(); return (pgid); } /* * Close a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct close_args { int fd; }; #endif /* * MPSAFE */ /* ARGSUSED */ int close(td, uap) struct thread *td; struct close_args *uap; { struct filedesc *fdp; struct file *fp; int fd, error; int holdleaders; fd = uap->fd; error = 0; holdleaders = 0; fdp = td->td_proc->p_fd; mtx_lock(&Giant); FILEDESC_LOCK(fdp); if ((unsigned)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) { FILEDESC_UNLOCK(fdp); mtx_unlock(&Giant); return (EBADF); } fdp->fd_ofiles[fd] = NULL; fdp->fd_ofileflags[fd] = 0; fdunused(fdp, fd); if (td->td_proc->p_fdtol != NULL) { /* * Ask fdfree() to sleep to ensure that all relevant * process leaders can be traversed in closef(). */ fdp->fd_holdleaderscount++; holdleaders = 1; } /* * we now hold the fp reference that used to be owned by the descriptor * array. */ if (fd < fdp->fd_knlistsize) { FILEDESC_UNLOCK(fdp); knote_fdclose(td, fd); } else FILEDESC_UNLOCK(fdp); error = closef(fp, td); mtx_unlock(&Giant); if (holdleaders) { FILEDESC_LOCK(fdp); fdp->fd_holdleaderscount--; if (fdp->fd_holdleaderscount == 0 && fdp->fd_holdleaderswakeup != 0) { fdp->fd_holdleaderswakeup = 0; wakeup(&fdp->fd_holdleaderscount); } FILEDESC_UNLOCK(fdp); } return (error); } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Return status information about a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct ofstat_args { int fd; struct ostat *sb; }; #endif /* * MPSAFE */ /* ARGSUSED */ int ofstat(td, uap) struct thread *td; struct ofstat_args *uap; { struct file *fp; struct stat ub; struct ostat oub; int error; if ((error = fget(td, uap->fd, &fp)) != 0) goto done2; mtx_lock(&Giant); error = fo_stat(fp, &ub, td->td_ucred, td); mtx_unlock(&Giant); if (error == 0) { cvtstat(&ub, &oub); error = copyout(&oub, uap->sb, sizeof(oub)); } fdrop(fp, td); done2: return (error); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ /* * Return status information about a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fstat_args { int fd; struct stat *sb; }; #endif /* * MPSAFE */ /* ARGSUSED */ int fstat(td, uap) struct thread *td; struct fstat_args *uap; { struct file *fp; struct stat ub; int error; if ((error = fget(td, uap->fd, &fp)) != 0) goto done2; mtx_lock(&Giant); error = fo_stat(fp, &ub, td->td_ucred, td); mtx_unlock(&Giant); if (error == 0) error = copyout(&ub, uap->sb, sizeof(ub)); fdrop(fp, td); done2: return (error); } /* * Return status information about a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct nfstat_args { int fd; struct nstat *sb; }; #endif /* * MPSAFE */ /* ARGSUSED */ int nfstat(td, uap) struct thread *td; struct nfstat_args *uap; { struct file *fp; struct stat ub; struct nstat nub; int error; if ((error = fget(td, uap->fd, &fp)) != 0) goto done2; mtx_lock(&Giant); error = fo_stat(fp, &ub, td->td_ucred, td); mtx_unlock(&Giant); if (error == 0) { cvtnstat(&ub, &nub); error = copyout(&nub, uap->sb, sizeof(nub)); } fdrop(fp, td); done2: return (error); } /* * Return pathconf information about a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fpathconf_args { int fd; int name; }; #endif /* * MPSAFE */ /* ARGSUSED */ int fpathconf(td, uap) struct thread *td; struct fpathconf_args *uap; { struct file *fp; struct vnode *vp; int error; if ((error = fget(td, uap->fd, &fp)) != 0) return (error); /* If asynchronous I/O is available, it works for all descriptors. */ if (uap->name == _PC_ASYNC_IO) { td->td_retval[0] = async_io_version; goto out; } vp = fp->f_vnode; if (vp != NULL) { mtx_lock(&Giant); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = VOP_PATHCONF(vp, uap->name, td->td_retval); VOP_UNLOCK(vp, 0, td); mtx_unlock(&Giant); } else if (fp->f_type == DTYPE_PIPE || fp->f_type == DTYPE_SOCKET) { if (uap->name != _PC_PIPE_BUF) { error = EINVAL; } else { td->td_retval[0] = PIPE_BUF; error = 0; } } else { error = EOPNOTSUPP; } out: fdrop(fp, td); return (error); } /* * Grow the file table to accomodate (at least) nfd descriptors. This may * block and drop the filedesc lock, but it will reacquire it before * returing. */ static void fdgrowtable(struct filedesc *fdp, int nfd) { struct file **ntable; char *nfileflags; int nnfiles, onfiles; NDSLOTTYPE *nmap; FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); KASSERT(fdp->fd_nfiles > 0, ("zero-length file table")); /* compute the size of the new table */ onfiles = fdp->fd_nfiles; nnfiles = NDSLOTS(nfd) * NDENTRIES; /* round up */ if (nnfiles <= onfiles) /* the table is already large enough */ return; /* allocate a new table and (if required) new bitmaps */ FILEDESC_UNLOCK(fdp); MALLOC(ntable, struct file **, nnfiles * OFILESIZE, M_FILEDESC, M_ZERO | M_WAITOK); nfileflags = (char *)&ntable[nnfiles]; if (NDSLOTS(nnfiles) > NDSLOTS(onfiles)) MALLOC(nmap, NDSLOTTYPE *, NDSLOTS(nnfiles) * NDSLOTSIZE, M_FILEDESC, M_ZERO | M_WAITOK); else nmap = NULL; FILEDESC_LOCK(fdp); /* * We now have new tables ready to go. Since we dropped the * filedesc lock to call malloc(), watch out for a race. */ onfiles = fdp->fd_nfiles; if (onfiles >= nnfiles) { /* we lost the race, but that's OK */ free(ntable, M_FILEDESC); if (nmap != NULL) free(nmap, M_FILEDESC); return; } bcopy(fdp->fd_ofiles, ntable, onfiles * sizeof(*ntable)); bcopy(fdp->fd_ofileflags, nfileflags, onfiles); if (onfiles > NDFILE) free(fdp->fd_ofiles, M_FILEDESC); fdp->fd_ofiles = ntable; fdp->fd_ofileflags = nfileflags; if (NDSLOTS(nnfiles) > NDSLOTS(onfiles)) { bcopy(fdp->fd_map, nmap, NDSLOTS(onfiles) * sizeof(*nmap)); if (NDSLOTS(onfiles) > NDSLOTS(NDFILE)) free(fdp->fd_map, M_FILEDESC); fdp->fd_map = nmap; } fdp->fd_nfiles = nnfiles; } /* * Allocate a file descriptor for the process. */ int fdalloc(struct thread *td, int minfd, int *result) { struct proc *p = td->td_proc; struct filedesc *fdp = p->p_fd; int fd = -1, maxfd; FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); PROC_LOCK(p); maxfd = min((int)lim_cur(p, RLIMIT_NOFILE), maxfilesperproc); PROC_UNLOCK(p); /* * Search the bitmap for a free descriptor. If none is found, try * to grow the file table. Keep at it until we either get a file * descriptor or run into process or system limits; fdgrowtable() * may drop the filedesc lock, so we're in a race. */ for (;;) { fd = fd_first_free(fdp, minfd, fdp->fd_nfiles); if (fd >= maxfd) return (EMFILE); if (fd < fdp->fd_nfiles) break; fdgrowtable(fdp, min(fdp->fd_nfiles * 2, maxfd)); } /* * Perform some sanity checks, then mark the file descriptor as * used and return it to the caller. */ KASSERT(!fdisused(fdp, fd), ("fd_first_free() returned non-free descriptor")); KASSERT(fdp->fd_ofiles[fd] == NULL, ("free descriptor isn't")); fdp->fd_ofileflags[fd] = 0; /* XXX needed? */ fdused(fdp, fd); fdp->fd_freefile = fd_first_free(fdp, fd, fdp->fd_nfiles); *result = fd; return (0); } /* * Check to see whether n user file descriptors * are available to the process p. */ int fdavail(td, n) struct thread *td; int n; { struct proc *p = td->td_proc; struct filedesc *fdp = td->td_proc->p_fd; struct file **fpp; int i, lim, last; FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); PROC_LOCK(p); lim = min((int)lim_cur(p, RLIMIT_NOFILE), maxfilesperproc); PROC_UNLOCK(p); if ((i = lim - fdp->fd_nfiles) > 0 && (n -= i) <= 0) return (1); last = min(fdp->fd_nfiles, lim); fpp = &fdp->fd_ofiles[fdp->fd_freefile]; for (i = last - fdp->fd_freefile; --i >= 0; fpp++) { if (*fpp == NULL && --n <= 0) return (1); } return (0); } /* * Create a new open file structure and allocate * a file decriptor for the process that refers to it. * We add one reference to the file for the descriptor table * and one reference for resultfp. This is to prevent us being * prempted and the entry in the descriptor table closed after * we release the FILEDESC lock. */ int falloc(td, resultfp, resultfd) struct thread *td; struct file **resultfp; int *resultfd; { struct proc *p = td->td_proc; struct file *fp, *fq; int error, i; int maxuserfiles = maxfiles - (maxfiles / 20); static struct timeval lastfail; static int curfail; fp = uma_zalloc(file_zone, M_WAITOK | M_ZERO); sx_xlock(&filelist_lock); if ((nfiles >= maxuserfiles && td->td_ucred->cr_ruid != 0) || nfiles >= maxfiles) { if (ppsratecheck(&lastfail, &curfail, 1)) { printf("kern.maxfiles limit exceeded by uid %i, please see tuning(7).\n", td->td_ucred->cr_ruid); } sx_xunlock(&filelist_lock); uma_zfree(file_zone, fp); return (ENFILE); } nfiles++; /* * If the process has file descriptor zero open, add the new file * descriptor to the list of open files at that point, otherwise * put it at the front of the list of open files. */ fp->f_mtxp = mtx_pool_alloc(mtxpool_sleep); fp->f_count = 1; if (resultfp) fp->f_count++; fp->f_cred = crhold(td->td_ucred); fp->f_ops = &badfileops; FILEDESC_LOCK(p->p_fd); if ((fq = p->p_fd->fd_ofiles[0])) { LIST_INSERT_AFTER(fq, fp, f_list); } else { LIST_INSERT_HEAD(&filehead, fp, f_list); } sx_xunlock(&filelist_lock); if ((error = fdalloc(td, 0, &i))) { FILEDESC_UNLOCK(p->p_fd); fdrop(fp, td); if (resultfp) fdrop(fp, td); return (error); } p->p_fd->fd_ofiles[i] = fp; FILEDESC_UNLOCK(p->p_fd); if (resultfp) *resultfp = fp; if (resultfd) *resultfd = i; return (0); } /* * Free a file descriptor. */ void ffree(fp) struct file *fp; { KASSERT(fp->f_count == 0, ("ffree: fp_fcount not 0!")); sx_xlock(&filelist_lock); LIST_REMOVE(fp, f_list); nfiles--; sx_xunlock(&filelist_lock); crfree(fp->f_cred); uma_zfree(file_zone, fp); } /* * Build a new filedesc structure from another. * Copy the current, root, and jail root vnode references. */ struct filedesc * fdinit(fdp) struct filedesc *fdp; { struct filedesc0 *newfdp; FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); FILEDESC_UNLOCK(fdp); MALLOC(newfdp, struct filedesc0 *, sizeof(struct filedesc0), M_FILEDESC, M_WAITOK | M_ZERO); FILEDESC_LOCK(fdp); mtx_init(&newfdp->fd_fd.fd_mtx, FILEDESC_LOCK_DESC, NULL, MTX_DEF); newfdp->fd_fd.fd_cdir = fdp->fd_cdir; if (newfdp->fd_fd.fd_cdir) VREF(newfdp->fd_fd.fd_cdir); newfdp->fd_fd.fd_rdir = fdp->fd_rdir; if (newfdp->fd_fd.fd_rdir) VREF(newfdp->fd_fd.fd_rdir); newfdp->fd_fd.fd_jdir = fdp->fd_jdir; if (newfdp->fd_fd.fd_jdir) VREF(newfdp->fd_fd.fd_jdir); /* Create the file descriptor table. */ newfdp->fd_fd.fd_refcnt = 1; newfdp->fd_fd.fd_cmask = CMASK; newfdp->fd_fd.fd_ofiles = newfdp->fd_dfiles; newfdp->fd_fd.fd_ofileflags = newfdp->fd_dfileflags; newfdp->fd_fd.fd_nfiles = NDFILE; newfdp->fd_fd.fd_knlistsize = -1; newfdp->fd_fd.fd_map = newfdp->fd_dmap; return (&newfdp->fd_fd); } /* * Share a filedesc structure. */ struct filedesc * fdshare(fdp) struct filedesc *fdp; { FILEDESC_LOCK(fdp); fdp->fd_refcnt++; FILEDESC_UNLOCK(fdp); return (fdp); } /* * Copy a filedesc structure. * A NULL pointer in returns a NULL reference, this is to ease callers, * not catch errors. */ struct filedesc * fdcopy(fdp) struct filedesc *fdp; { struct filedesc *newfdp; int i; /* Certain daemons might not have file descriptors. */ if (fdp == NULL) return (NULL); FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); newfdp = fdinit(fdp); while (fdp->fd_lastfile >= newfdp->fd_nfiles) { FILEDESC_UNLOCK(fdp); FILEDESC_LOCK(newfdp); fdgrowtable(newfdp, fdp->fd_lastfile + 1); FILEDESC_UNLOCK(newfdp); FILEDESC_LOCK(fdp); } /* copy everything except kqueue descriptors */ newfdp->fd_freefile = -1; for (i = 0; i <= fdp->fd_lastfile; ++i) { if (fdisused(fdp, i) && fdp->fd_ofiles[i]->f_type != DTYPE_KQUEUE) { newfdp->fd_ofiles[i] = fdp->fd_ofiles[i]; newfdp->fd_ofileflags[i] = fdp->fd_ofileflags[i]; fhold(newfdp->fd_ofiles[i]); newfdp->fd_lastfile = i; } else { if (newfdp->fd_freefile == -1) newfdp->fd_freefile = i; } } FILEDESC_UNLOCK(fdp); FILEDESC_LOCK(newfdp); for (i = 0; i <= newfdp->fd_lastfile; ++i) if (newfdp->fd_ofiles[i] != NULL) fdused(newfdp, i); FILEDESC_UNLOCK(newfdp); FILEDESC_LOCK(fdp); if (newfdp->fd_freefile == -1) newfdp->fd_freefile = i; newfdp->fd_cmask = fdp->fd_cmask; return (newfdp); } /* A mutex to protect the association between a proc and filedesc. */ struct mtx fdesc_mtx; MTX_SYSINIT(fdesc, &fdesc_mtx, "fdesc", MTX_DEF); /* * Release a filedesc structure. */ void fdfree(td) struct thread *td; { struct filedesc *fdp; struct file **fpp; int i; struct filedesc_to_leader *fdtol; struct file *fp; struct vnode *vp; struct flock lf; /* Certain daemons might not have file descriptors. */ fdp = td->td_proc->p_fd; if (fdp == NULL) return; /* Check for special need to clear POSIX style locks */ fdtol = td->td_proc->p_fdtol; if (fdtol != NULL) { FILEDESC_LOCK(fdp); KASSERT(fdtol->fdl_refcount > 0, ("filedesc_to_refcount botch: fdl_refcount=%d", fdtol->fdl_refcount)); if (fdtol->fdl_refcount == 1 && (td->td_proc->p_leader->p_flag & P_ADVLOCK) != 0) { i = 0; fpp = fdp->fd_ofiles; for (i = 0, fpp = fdp->fd_ofiles; i <= fdp->fd_lastfile; i++, fpp++) { if (*fpp == NULL || (*fpp)->f_type != DTYPE_VNODE) continue; fp = *fpp; fhold(fp); FILEDESC_UNLOCK(fdp); lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_UNLCK; vp = fp->f_vnode; (void) VOP_ADVLOCK(vp, (caddr_t)td->td_proc-> p_leader, F_UNLCK, &lf, F_POSIX); FILEDESC_LOCK(fdp); fdrop(fp, td); fpp = fdp->fd_ofiles + i; } } retry: if (fdtol->fdl_refcount == 1) { if (fdp->fd_holdleaderscount > 0 && (td->td_proc->p_leader->p_flag & P_ADVLOCK) != 0) { /* * close() or do_dup() has cleared a reference * in a shared file descriptor table. */ fdp->fd_holdleaderswakeup = 1; msleep(&fdp->fd_holdleaderscount, &fdp->fd_mtx, PLOCK, "fdlhold", 0); goto retry; } if (fdtol->fdl_holdcount > 0) { /* * Ensure that fdtol->fdl_leader * remains valid in closef(). */ fdtol->fdl_wakeup = 1; msleep(fdtol, &fdp->fd_mtx, PLOCK, "fdlhold", 0); goto retry; } } fdtol->fdl_refcount--; if (fdtol->fdl_refcount == 0 && fdtol->fdl_holdcount == 0) { fdtol->fdl_next->fdl_prev = fdtol->fdl_prev; fdtol->fdl_prev->fdl_next = fdtol->fdl_next; } else fdtol = NULL; td->td_proc->p_fdtol = NULL; FILEDESC_UNLOCK(fdp); if (fdtol != NULL) FREE(fdtol, M_FILEDESC_TO_LEADER); } FILEDESC_LOCK(fdp); if (--fdp->fd_refcnt > 0) { FILEDESC_UNLOCK(fdp); return; } /* * We are the last reference to the structure, so we can * safely assume it will not change out from under us. */ FILEDESC_UNLOCK(fdp); fpp = fdp->fd_ofiles; for (i = fdp->fd_lastfile; i-- >= 0; fpp++) { if (*fpp) (void) closef(*fpp, td); } /* XXX This should happen earlier. */ mtx_lock(&fdesc_mtx); td->td_proc->p_fd = NULL; mtx_unlock(&fdesc_mtx); if (fdp->fd_nfiles > NDFILE) FREE(fdp->fd_ofiles, M_FILEDESC); if (NDSLOTS(fdp->fd_nfiles) > NDSLOTS(NDFILE)) FREE(fdp->fd_map, M_FILEDESC); if (fdp->fd_cdir) vrele(fdp->fd_cdir); if (fdp->fd_rdir) vrele(fdp->fd_rdir); if (fdp->fd_jdir) vrele(fdp->fd_jdir); if (fdp->fd_knlist) FREE(fdp->fd_knlist, M_KQUEUE); if (fdp->fd_knhash) FREE(fdp->fd_knhash, M_KQUEUE); mtx_destroy(&fdp->fd_mtx); FREE(fdp, M_FILEDESC); } /* * For setugid programs, we don't want to people to use that setugidness * to generate error messages which write to a file which otherwise would * otherwise be off-limits to the process. We check for filesystems where * the vnode can change out from under us after execve (like [lin]procfs). * * Since setugidsafety calls this only for fd 0, 1 and 2, this check is * sufficient. We also don't for check setugidness since we know we are. */ static int is_unsafe(struct file *fp) { if (fp->f_type == DTYPE_VNODE) { struct vnode *vp = fp->f_vnode; if ((vp->v_vflag & VV_PROCDEP) != 0) return (1); } return (0); } /* * Make this setguid thing safe, if at all possible. */ void setugidsafety(td) struct thread *td; { struct filedesc *fdp; int i; /* Certain daemons might not have file descriptors. */ fdp = td->td_proc->p_fd; if (fdp == NULL) return; /* * Note: fdp->fd_ofiles may be reallocated out from under us while * we are blocked in a close. Be careful! */ FILEDESC_LOCK(fdp); for (i = 0; i <= fdp->fd_lastfile; i++) { if (i > 2) break; if (fdp->fd_ofiles[i] && is_unsafe(fdp->fd_ofiles[i])) { struct file *fp; if (i < fdp->fd_knlistsize) { FILEDESC_UNLOCK(fdp); knote_fdclose(td, i); FILEDESC_LOCK(fdp); } /* * NULL-out descriptor prior to close to avoid * a race while close blocks. */ fp = fdp->fd_ofiles[i]; fdp->fd_ofiles[i] = NULL; fdp->fd_ofileflags[i] = 0; fdunused(fdp, i); FILEDESC_UNLOCK(fdp); (void) closef(fp, td); FILEDESC_LOCK(fdp); } } FILEDESC_UNLOCK(fdp); } /* * Close any files on exec? */ void fdcloseexec(td) struct thread *td; { struct filedesc *fdp; int i; /* Certain daemons might not have file descriptors. */ fdp = td->td_proc->p_fd; if (fdp == NULL) return; FILEDESC_LOCK(fdp); /* * We cannot cache fd_ofiles or fd_ofileflags since operations * may block and rip them out from under us. */ for (i = 0; i <= fdp->fd_lastfile; i++) { if (fdp->fd_ofiles[i] != NULL && (fdp->fd_ofileflags[i] & UF_EXCLOSE)) { struct file *fp; if (i < fdp->fd_knlistsize) { FILEDESC_UNLOCK(fdp); knote_fdclose(td, i); FILEDESC_LOCK(fdp); } /* * NULL-out descriptor prior to close to avoid * a race while close blocks. */ fp = fdp->fd_ofiles[i]; fdp->fd_ofiles[i] = NULL; fdp->fd_ofileflags[i] = 0; fdunused(fdp, i); FILEDESC_UNLOCK(fdp); (void) closef(fp, td); FILEDESC_LOCK(fdp); } } FILEDESC_UNLOCK(fdp); } /* * It is unsafe for set[ug]id processes to be started with file * descriptors 0..2 closed, as these descriptors are given implicit * significance in the Standard C library. fdcheckstd() will create a * descriptor referencing /dev/null for each of stdin, stdout, and * stderr that is not already open. */ int fdcheckstd(td) struct thread *td; { struct nameidata nd; struct filedesc *fdp; struct file *fp; register_t retval; int fd, i, error, flags, devnull; fdp = td->td_proc->p_fd; if (fdp == NULL) return (0); KASSERT(fdp->fd_refcnt == 1, ("the fdtable should not be shared")); devnull = -1; error = 0; for (i = 0; i < 3; i++) { if (fdp->fd_ofiles[i] != NULL) continue; if (devnull < 0) { error = falloc(td, &fp, &fd); if (error != 0) break; /* Note extra ref on `fp' held for us by falloc(). */ KASSERT(fd == i, ("oof, we didn't get our fd")); NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, "/dev/null", td); flags = FREAD | FWRITE; error = vn_open(&nd, &flags, 0, -1); if (error != 0) { /* * Someone may have closed the entry in the * file descriptor table, so check it hasn't * changed before dropping the reference count. */ FILEDESC_LOCK(fdp); KASSERT(fdp->fd_ofiles[fd] == fp, ("table not shared, how did it change?")); fdp->fd_ofiles[fd] = NULL; fdunused(fdp, fd); FILEDESC_UNLOCK(fdp); fdrop(fp, td); fdrop(fp, td); break; } NDFREE(&nd, NDF_ONLY_PNBUF); fp->f_vnode = nd.ni_vp; fp->f_data = nd.ni_vp; fp->f_flag = flags; fp->f_ops = &vnops; fp->f_type = DTYPE_VNODE; VOP_UNLOCK(nd.ni_vp, 0, td); devnull = fd; fdrop(fp, td); } else { error = do_dup(td, DUP_FIXED, devnull, i, &retval); if (error != 0) break; } } return (error); } /* * Internal form of close. * Decrement reference count on file structure. * Note: td may be NULL when closing a file * that was being passed in a message. */ int closef(fp, td) struct file *fp; struct thread *td; { struct vnode *vp; struct flock lf; struct filedesc_to_leader *fdtol; struct filedesc *fdp; if (fp == NULL) return (0); /* * POSIX record locking dictates that any close releases ALL * locks owned by this process. This is handled by setting * a flag in the unlock to free ONLY locks obeying POSIX * semantics, and not to free BSD-style file locks. * If the descriptor was in a message, POSIX-style locks * aren't passed with the descriptor. */ if (td != NULL && fp->f_type == DTYPE_VNODE) { if ((td->td_proc->p_leader->p_flag & P_ADVLOCK) != 0) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_UNLCK; vp = fp->f_vnode; (void) VOP_ADVLOCK(vp, (caddr_t)td->td_proc->p_leader, F_UNLCK, &lf, F_POSIX); } fdtol = td->td_proc->p_fdtol; if (fdtol != NULL) { /* * Handle special case where file descriptor table * is shared between multiple process leaders. */ fdp = td->td_proc->p_fd; FILEDESC_LOCK(fdp); for (fdtol = fdtol->fdl_next; fdtol != td->td_proc->p_fdtol; fdtol = fdtol->fdl_next) { if ((fdtol->fdl_leader->p_flag & P_ADVLOCK) == 0) continue; fdtol->fdl_holdcount++; FILEDESC_UNLOCK(fdp); lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_UNLCK; vp = fp->f_vnode; (void) VOP_ADVLOCK(vp, (caddr_t)fdtol->fdl_leader, F_UNLCK, &lf, F_POSIX); FILEDESC_LOCK(fdp); fdtol->fdl_holdcount--; if (fdtol->fdl_holdcount == 0 && fdtol->fdl_wakeup != 0) { fdtol->fdl_wakeup = 0; wakeup(fdtol); } } FILEDESC_UNLOCK(fdp); } } return (fdrop(fp, td)); } /* * Drop reference on struct file passed in, may call closef if the * reference hits zero. */ int fdrop(fp, td) struct file *fp; struct thread *td; { FILE_LOCK(fp); return (fdrop_locked(fp, td)); } /* * Extract the file pointer associated with the specified descriptor for * the current user process. * * If the descriptor doesn't exist, EBADF is returned. * * If the descriptor exists but doesn't match 'flags' then * return EBADF for read attempts and EINVAL for write attempts. * * If 'hold' is set (non-zero) the file's refcount will be bumped on return. * It should be droped with fdrop(). * If it is not set, then the refcount will not be bumped however the * thread's filedesc struct will be returned locked (for fgetsock). * * If an error occured the non-zero error is returned and *fpp is set to NULL. * Otherwise *fpp is set and zero is returned. */ static __inline int _fget(struct thread *td, int fd, struct file **fpp, int flags, int hold) { struct filedesc *fdp; struct file *fp; *fpp = NULL; if (td == NULL || (fdp = td->td_proc->p_fd) == NULL) return (EBADF); FILEDESC_LOCK(fdp); if ((fp = fget_locked(fdp, fd)) == NULL || fp->f_ops == &badfileops) { FILEDESC_UNLOCK(fdp); return (EBADF); } /* * Note: FREAD failures returns EBADF to maintain backwards * compatibility with what routines returned before. * * Only one flag, or 0, may be specified. */ if (flags == FREAD && (fp->f_flag & FREAD) == 0) { FILEDESC_UNLOCK(fdp); return (EBADF); } if (flags == FWRITE && (fp->f_flag & FWRITE) == 0) { FILEDESC_UNLOCK(fdp); return (EINVAL); } if (hold) { fhold(fp); FILEDESC_UNLOCK(fdp); } *fpp = fp; return (0); } int fget(struct thread *td, int fd, struct file **fpp) { return(_fget(td, fd, fpp, 0, 1)); } int fget_read(struct thread *td, int fd, struct file **fpp) { return(_fget(td, fd, fpp, FREAD, 1)); } int fget_write(struct thread *td, int fd, struct file **fpp) { return(_fget(td, fd, fpp, FWRITE, 1)); } /* * Like fget() but loads the underlying vnode, or returns an error if * the descriptor does not represent a vnode. Note that pipes use vnodes * but never have VM objects (so VOP_GETVOBJECT() calls will return an * error). The returned vnode will be vref()d. */ static __inline int _fgetvp(struct thread *td, int fd, struct vnode **vpp, int flags) { struct file *fp; int error; *vpp = NULL; if ((error = _fget(td, fd, &fp, 0, 0)) != 0) return (error); if (fp->f_vnode == NULL) { error = EINVAL; } else { *vpp = fp->f_vnode; vref(*vpp); } FILEDESC_UNLOCK(td->td_proc->p_fd); return (error); } int fgetvp(struct thread *td, int fd, struct vnode **vpp) { return (_fgetvp(td, fd, vpp, 0)); } int fgetvp_read(struct thread *td, int fd, struct vnode **vpp) { return (_fgetvp(td, fd, vpp, FREAD)); } int fgetvp_write(struct thread *td, int fd, struct vnode **vpp) { return (_fgetvp(td, fd, vpp, FWRITE)); } /* * Like fget() but loads the underlying socket, or returns an error if * the descriptor does not represent a socket. * * We bump the ref count on the returned socket. XXX Also obtain the SX * lock in the future. */ int fgetsock(struct thread *td, int fd, struct socket **spp, u_int *fflagp) { struct file *fp; int error; *spp = NULL; if (fflagp != NULL) *fflagp = 0; if ((error = _fget(td, fd, &fp, 0, 0)) != 0) return (error); if (fp->f_type != DTYPE_SOCKET) { error = ENOTSOCK; } else { *spp = fp->f_data; if (fflagp) *fflagp = fp->f_flag; soref(*spp); } FILEDESC_UNLOCK(td->td_proc->p_fd); return (error); } /* * Drop the reference count on the the socket and XXX release the SX lock in * the future. The last reference closes the socket. */ void fputsock(struct socket *so) { NET_ASSERT_GIANT(); sorele(so); } /* * Drop reference on struct file passed in, may call closef if the * reference hits zero. * Expects struct file locked, and will unlock it. */ int fdrop_locked(fp, td) struct file *fp; struct thread *td; { int error; FILE_LOCK_ASSERT(fp, MA_OWNED); if (--fp->f_count > 0) { FILE_UNLOCK(fp); return (0); } /* We have the last ref so we can proceed without the file lock. */ FILE_UNLOCK(fp); if (fp->f_count < 0) panic("fdrop: count < 0"); mtx_lock(&Giant); if (fp->f_ops != &badfileops) error = fo_close(fp, td); else error = 0; ffree(fp); mtx_unlock(&Giant); return (error); } /* * Apply an advisory lock on a file descriptor. * * Just attempt to get a record lock of the requested type on * the entire file (l_whence = SEEK_SET, l_start = 0, l_len = 0). */ #ifndef _SYS_SYSPROTO_H_ struct flock_args { int fd; int how; }; #endif /* * MPSAFE */ /* ARGSUSED */ int flock(td, uap) struct thread *td; struct flock_args *uap; { struct file *fp; struct vnode *vp; struct flock lf; int error; if ((error = fget(td, uap->fd, &fp)) != 0) return (error); if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); return (EOPNOTSUPP); } mtx_lock(&Giant); vp = fp->f_vnode; lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (uap->how & LOCK_UN) { lf.l_type = F_UNLCK; FILE_LOCK(fp); fp->f_flag &= ~FHASLOCK; FILE_UNLOCK(fp); error = VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, F_FLOCK); goto done2; } if (uap->how & LOCK_EX) lf.l_type = F_WRLCK; else if (uap->how & LOCK_SH) lf.l_type = F_RDLCK; else { error = EBADF; goto done2; } FILE_LOCK(fp); fp->f_flag |= FHASLOCK; FILE_UNLOCK(fp); error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, (uap->how & LOCK_NB) ? F_FLOCK : F_FLOCK | F_WAIT); done2: fdrop(fp, td); mtx_unlock(&Giant); return (error); } /* * File Descriptor pseudo-device driver (/dev/fd/). * * Opening minor device N dup()s the file (if any) connected to file * descriptor N belonging to the calling process. Note that this driver * consists of only the ``open()'' routine, because all subsequent * references to this file will be direct to the other driver. */ /* ARGSUSED */ static int fdopen(dev, mode, type, td) dev_t dev; int mode, type; struct thread *td; { /* * XXX Kludge: set curthread->td_dupfd to contain the value of the * the file descriptor being sought for duplication. The error * return ensures that the vnode for this device will be released * by vn_open. Open will detect this special error and take the * actions in dupfdopen below. Other callers of vn_open or VOP_OPEN * will simply report the error. */ td->td_dupfd = dev2unit(dev); return (ENODEV); } /* * Duplicate the specified descriptor to a free descriptor. */ int dupfdopen(td, fdp, indx, dfd, mode, error) struct thread *td; struct filedesc *fdp; int indx, dfd; int mode; int error; { struct file *wfp; struct file *fp; /* * If the to-be-dup'd fd number is greater than the allowed number * of file descriptors, or the fd to be dup'd has already been * closed, then reject. */ FILEDESC_LOCK(fdp); if (dfd < 0 || dfd >= fdp->fd_nfiles || (wfp = fdp->fd_ofiles[dfd]) == NULL) { FILEDESC_UNLOCK(fdp); return (EBADF); } /* * There are two cases of interest here. * * For ENODEV simply dup (dfd) to file descriptor * (indx) and return. * * For ENXIO steal away the file structure from (dfd) and * store it in (indx). (dfd) is effectively closed by * this operation. * * Any other error code is just returned. */ switch (error) { case ENODEV: /* * Check that the mode the file is being opened for is a * subset of the mode of the existing descriptor. */ FILE_LOCK(wfp); if (((mode & (FREAD|FWRITE)) | wfp->f_flag) != wfp->f_flag) { FILE_UNLOCK(wfp); FILEDESC_UNLOCK(fdp); return (EACCES); } fp = fdp->fd_ofiles[indx]; fdp->fd_ofiles[indx] = wfp; fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd]; if (fp == NULL) fdused(fdp, indx); fhold_locked(wfp); FILE_UNLOCK(wfp); if (fp != NULL) FILE_LOCK(fp); FILEDESC_UNLOCK(fdp); /* * We now own the reference to fp that the ofiles[] array * used to own. Release it. */ if (fp != NULL) fdrop_locked(fp, td); return (0); case ENXIO: /* * Steal away the file pointer from dfd and stuff it into indx. */ fp = fdp->fd_ofiles[indx]; fdp->fd_ofiles[indx] = fdp->fd_ofiles[dfd]; fdp->fd_ofiles[dfd] = NULL; fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd]; fdp->fd_ofileflags[dfd] = 0; fdunused(fdp, dfd); if (fp == NULL) fdused(fdp, indx); if (fp != NULL) FILE_LOCK(fp); FILEDESC_UNLOCK(fdp); /* * we now own the reference to fp that the ofiles[] array * used to own. Release it. */ if (fp != NULL) fdrop_locked(fp, td); return (0); default: FILEDESC_UNLOCK(fdp); return (error); } /* NOTREACHED */ } struct filedesc_to_leader * filedesc_to_leader_alloc(struct filedesc_to_leader *old, struct filedesc *fdp, struct proc *leader) { struct filedesc_to_leader *fdtol; MALLOC(fdtol, struct filedesc_to_leader *, sizeof(struct filedesc_to_leader), M_FILEDESC_TO_LEADER, M_WAITOK); fdtol->fdl_refcount = 1; fdtol->fdl_holdcount = 0; fdtol->fdl_wakeup = 0; fdtol->fdl_leader = leader; if (old != NULL) { FILEDESC_LOCK(fdp); fdtol->fdl_next = old->fdl_next; fdtol->fdl_prev = old; old->fdl_next = fdtol; fdtol->fdl_next->fdl_prev = fdtol; FILEDESC_UNLOCK(fdp); } else { fdtol->fdl_next = fdtol; fdtol->fdl_prev = fdtol; } return (fdtol); } /* * Get file structures. */ static int sysctl_kern_file(SYSCTL_HANDLER_ARGS) { struct xfile xf; struct filedesc *fdp; struct file *fp; struct proc *p; int error, n; /* * Note: because the number of file descriptors is calculated * in different ways for sizing vs returning the data, * there is information leakage from the first loop. However, * it is of a similar order of magnitude to the leakage from * global system statistics such as kern.openfiles. */ error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); if (req->oldptr == NULL) { n = 16; /* A slight overestimate. */ sx_slock(&filelist_lock); LIST_FOREACH(fp, &filehead, f_list) { /* * We should grab the lock, but this is an * estimate, so does it really matter? */ /* mtx_lock(fp->f_mtxp); */ n += fp->f_count; /* mtx_unlock(f->f_mtxp); */ } sx_sunlock(&filelist_lock); return (SYSCTL_OUT(req, 0, n * sizeof(xf))); } error = 0; bzero(&xf, sizeof(xf)); xf.xf_size = sizeof(xf); sx_slock(&allproc_lock); LIST_FOREACH(p, &allproc, p_list) { PROC_LOCK(p); if (p_cansee(req->td, p) != 0) { PROC_UNLOCK(p); continue; } xf.xf_pid = p->p_pid; xf.xf_uid = p->p_ucred->cr_uid; PROC_UNLOCK(p); mtx_lock(&fdesc_mtx); if ((fdp = p->p_fd) == NULL) { mtx_unlock(&fdesc_mtx); continue; } FILEDESC_LOCK(fdp); for (n = 0; n < fdp->fd_nfiles; ++n) { if ((fp = fdp->fd_ofiles[n]) == NULL) continue; xf.xf_fd = n; xf.xf_file = fp; xf.xf_data = fp->f_data; xf.xf_type = fp->f_type; xf.xf_count = fp->f_count; xf.xf_msgcount = fp->f_msgcount; xf.xf_offset = fp->f_offset; xf.xf_flag = fp->f_flag; error = SYSCTL_OUT(req, &xf, sizeof(xf)); if (error) break; } FILEDESC_UNLOCK(fdp); mtx_unlock(&fdesc_mtx); if (error) break; } sx_sunlock(&allproc_lock); return (error); } SYSCTL_PROC(_kern, KERN_FILE, file, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_kern_file, "S,xfile", "Entire file table"); SYSCTL_INT(_kern, KERN_MAXFILESPERPROC, maxfilesperproc, CTLFLAG_RW, &maxfilesperproc, 0, "Maximum files allowed open per process"); SYSCTL_INT(_kern, KERN_MAXFILES, maxfiles, CTLFLAG_RW, &maxfiles, 0, "Maximum number of files"); SYSCTL_INT(_kern, OID_AUTO, openfiles, CTLFLAG_RD, &nfiles, 0, "System-wide number of open files"); static void fildesc_drvinit(void *unused) { dev_t dev; dev = make_dev(&fildesc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "fd/0"); make_dev_alias(dev, "stdin"); dev = make_dev(&fildesc_cdevsw, 1, UID_ROOT, GID_WHEEL, 0666, "fd/1"); make_dev_alias(dev, "stdout"); dev = make_dev(&fildesc_cdevsw, 2, UID_ROOT, GID_WHEEL, 0666, "fd/2"); make_dev_alias(dev, "stderr"); } static fo_rdwr_t badfo_readwrite; static fo_ioctl_t badfo_ioctl; static fo_poll_t badfo_poll; static fo_kqfilter_t badfo_kqfilter; static fo_stat_t badfo_stat; static fo_close_t badfo_close; struct fileops badfileops = { .fo_read = badfo_readwrite, .fo_write = badfo_readwrite, .fo_ioctl = badfo_ioctl, .fo_poll = badfo_poll, .fo_kqfilter = badfo_kqfilter, .fo_stat = badfo_stat, .fo_close = badfo_close, }; static int badfo_readwrite(fp, uio, active_cred, flags, td) struct file *fp; struct uio *uio; struct ucred *active_cred; struct thread *td; int flags; { return (EBADF); } static int badfo_ioctl(fp, com, data, active_cred, td) struct file *fp; u_long com; void *data; struct ucred *active_cred; struct thread *td; { return (EBADF); } static int badfo_poll(fp, events, active_cred, td) struct file *fp; int events; struct ucred *active_cred; struct thread *td; { return (0); } static int badfo_kqfilter(fp, kn) struct file *fp; struct knote *kn; { return (0); } static int badfo_stat(fp, sb, active_cred, td) struct file *fp; struct stat *sb; struct ucred *active_cred; struct thread *td; { return (EBADF); } static int badfo_close(fp, td) struct file *fp; struct thread *td; { return (EBADF); } SYSINIT(fildescdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR, fildesc_drvinit,NULL) static void filelistinit(void *); SYSINIT(select, SI_SUB_LOCK, SI_ORDER_FIRST, filelistinit, NULL) /* ARGSUSED*/ static void filelistinit(dummy) void *dummy; { file_zone = uma_zcreate("Files", sizeof(struct file), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); sx_init(&filelist_lock, "filelist lock"); mtx_init(&sigio_lock, "sigio lock", NULL, MTX_DEF); } Index: head/sys/kern/kern_prot.c =================================================================== --- head/sys/kern/kern_prot.c (revision 130343) +++ head/sys/kern/kern_prot.c (revision 130344) @@ -1,2030 +1,2030 @@ /* * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * Copyright (c) 2000-2001 Robert N. M. Watson. All rights reserved. * * 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. * 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. * * @(#)kern_prot.c 8.6 (Berkeley) 1/21/94 */ /* * System calls related to processes and protection */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_CRED, "cred", "credentials"); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, bsd, CTLFLAG_RW, 0, "BSD security policy"); #ifndef _SYS_SYSPROTO_H_ struct getpid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getpid(struct thread *td, struct getpid_args *uap) { struct proc *p = td->td_proc; td->td_retval[0] = p->p_pid; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) PROC_LOCK(p); td->td_retval[1] = p->p_pptr->p_pid; PROC_UNLOCK(p); #endif return (0); } #ifndef _SYS_SYSPROTO_H_ struct getppid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getppid(struct thread *td, struct getppid_args *uap) { struct proc *p = td->td_proc; PROC_LOCK(p); td->td_retval[0] = p->p_pptr->p_pid; PROC_UNLOCK(p); return (0); } /* * Get process group ID; note that POSIX getpgrp takes no parameter. */ #ifndef _SYS_SYSPROTO_H_ struct getpgrp_args { int dummy; }; #endif /* * MPSAFE */ int getpgrp(struct thread *td, struct getpgrp_args *uap) { struct proc *p = td->td_proc; PROC_LOCK(p); td->td_retval[0] = p->p_pgrp->pg_id; PROC_UNLOCK(p); return (0); } /* Get an arbitary pid's process group id */ #ifndef _SYS_SYSPROTO_H_ struct getpgid_args { pid_t pid; }; #endif /* * MPSAFE */ int getpgid(struct thread *td, struct getpgid_args *uap) { struct proc *p; int error; if (uap->pid == 0) { p = td->td_proc; PROC_LOCK(p); } else { p = pfind(uap->pid); if (p == NULL) return (ESRCH); error = p_cansee(td, p); if (error) { PROC_UNLOCK(p); return (error); } } td->td_retval[0] = p->p_pgrp->pg_id; PROC_UNLOCK(p); return (0); } /* * Get an arbitary pid's session id. */ #ifndef _SYS_SYSPROTO_H_ struct getsid_args { pid_t pid; }; #endif /* * MPSAFE */ int getsid(struct thread *td, struct getsid_args *uap) { struct proc *p; int error; if (uap->pid == 0) { p = td->td_proc; PROC_LOCK(p); } else { p = pfind(uap->pid); if (p == NULL) return (ESRCH); error = p_cansee(td, p); if (error) { PROC_UNLOCK(p); return (error); } } td->td_retval[0] = p->p_session->s_sid; PROC_UNLOCK(p); return (0); } #ifndef _SYS_SYSPROTO_H_ struct getuid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getuid(struct thread *td, struct getuid_args *uap) { td->td_retval[0] = td->td_ucred->cr_ruid; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_retval[1] = td->td_ucred->cr_uid; #endif return (0); } #ifndef _SYS_SYSPROTO_H_ struct geteuid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int geteuid(struct thread *td, struct geteuid_args *uap) { td->td_retval[0] = td->td_ucred->cr_uid; return (0); } #ifndef _SYS_SYSPROTO_H_ struct getgid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getgid(struct thread *td, struct getgid_args *uap) { td->td_retval[0] = td->td_ucred->cr_rgid; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_retval[1] = td->td_ucred->cr_groups[0]; #endif return (0); } /* * Get effective group ID. The "egid" is groups[0], and could be obtained * via getgroups. This syscall exists because it is somewhat painful to do * correctly in a library function. */ #ifndef _SYS_SYSPROTO_H_ struct getegid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getegid(struct thread *td, struct getegid_args *uap) { td->td_retval[0] = td->td_ucred->cr_groups[0]; return (0); } #ifndef _SYS_SYSPROTO_H_ struct getgroups_args { u_int gidsetsize; gid_t *gidset; }; #endif /* * MPSAFE */ int getgroups(struct thread *td, register struct getgroups_args *uap) { struct ucred *cred; u_int ngrp; int error; cred = td->td_ucred; if ((ngrp = uap->gidsetsize) == 0) { td->td_retval[0] = cred->cr_ngroups; return (0); } if (ngrp < cred->cr_ngroups) return (EINVAL); ngrp = cred->cr_ngroups; error = copyout(cred->cr_groups, uap->gidset, ngrp * sizeof(gid_t)); if (error == 0) td->td_retval[0] = ngrp; return (error); } #ifndef _SYS_SYSPROTO_H_ struct setsid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setsid(register struct thread *td, struct setsid_args *uap) { struct pgrp *pgrp; int error; struct proc *p = td->td_proc; struct pgrp *newpgrp; struct session *newsess; error = 0; pgrp = NULL; MALLOC(newpgrp, struct pgrp *, sizeof(struct pgrp), M_PGRP, M_WAITOK | M_ZERO); MALLOC(newsess, struct session *, sizeof(struct session), M_SESSION, M_WAITOK | M_ZERO); sx_xlock(&proctree_lock); if (p->p_pgid == p->p_pid || (pgrp = pgfind(p->p_pid)) != NULL) { if (pgrp != NULL) PGRP_UNLOCK(pgrp); error = EPERM; } else { (void)enterpgrp(p, p->p_pid, newpgrp, newsess); td->td_retval[0] = p->p_pid; newpgrp = NULL; newsess = NULL; } sx_xunlock(&proctree_lock); if (newpgrp != NULL) FREE(newpgrp, M_PGRP); if (newsess != NULL) FREE(newsess, M_SESSION); return (error); } /* * set process group (setpgid/old setpgrp) * * caller does setpgid(targpid, targpgid) * * pid must be caller or child of caller (ESRCH) * if a child * pid must be in same session (EPERM) * pid can't have done an exec (EACCES) * if pgid != pid * there must exist some pid in same session having pgid (EPERM) * pid must not be session leader (EPERM) */ #ifndef _SYS_SYSPROTO_H_ struct setpgid_args { int pid; /* target process id */ int pgid; /* target pgrp id */ }; #endif /* * MPSAFE */ /* ARGSUSED */ int setpgid(struct thread *td, register struct setpgid_args *uap) { struct proc *curp = td->td_proc; register struct proc *targp; /* target process */ register struct pgrp *pgrp; /* target pgrp */ int error; struct pgrp *newpgrp; if (uap->pgid < 0) return (EINVAL); error = 0; MALLOC(newpgrp, struct pgrp *, sizeof(struct pgrp), M_PGRP, M_WAITOK | M_ZERO); sx_xlock(&proctree_lock); if (uap->pid != 0 && uap->pid != curp->p_pid) { if ((targp = pfind(uap->pid)) == NULL) { error = ESRCH; goto done; } if (!inferior(targp)) { PROC_UNLOCK(targp); error = ESRCH; goto done; } if ((error = p_cansee(curthread, targp))) { PROC_UNLOCK(targp); goto done; } if (targp->p_pgrp == NULL || targp->p_session != curp->p_session) { PROC_UNLOCK(targp); error = EPERM; goto done; } if (targp->p_flag & P_EXEC) { PROC_UNLOCK(targp); error = EACCES; goto done; } PROC_UNLOCK(targp); } else targp = curp; if (SESS_LEADER(targp)) { error = EPERM; goto done; } if (uap->pgid == 0) uap->pgid = targp->p_pid; if ((pgrp = pgfind(uap->pgid)) == NULL) { if (uap->pgid == targp->p_pid) { error = enterpgrp(targp, uap->pgid, newpgrp, NULL); if (error == 0) newpgrp = NULL; } else error = EPERM; } else { if (pgrp == targp->p_pgrp) { PGRP_UNLOCK(pgrp); goto done; } if (pgrp->pg_id != targp->p_pid && pgrp->pg_session != curp->p_session) { PGRP_UNLOCK(pgrp); error = EPERM; goto done; } PGRP_UNLOCK(pgrp); error = enterthispgrp(targp, pgrp); } done: sx_xunlock(&proctree_lock); KASSERT((error == 0) || (newpgrp != NULL), ("setpgid failed and newpgrp is NULL")); if (newpgrp != NULL) FREE(newpgrp, M_PGRP); return (error); } /* * Use the clause in B.4.2.2 that allows setuid/setgid to be 4.2/4.3BSD * compatible. It says that setting the uid/gid to euid/egid is a special * case of "appropriate privilege". Once the rules are expanded out, this * basically means that setuid(nnn) sets all three id's, in all permitted * cases unless _POSIX_SAVED_IDS is enabled. In that case, setuid(getuid()) * does not set the saved id - this is dangerous for traditional BSD * programs. For this reason, we *really* do not want to set * _POSIX_SAVED_IDS and do not want to clear POSIX_APPENDIX_B_4_2_2. */ #define POSIX_APPENDIX_B_4_2_2 #ifndef _SYS_SYSPROTO_H_ struct setuid_args { uid_t uid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setuid(struct thread *td, struct setuid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; uid_t uid; struct uidinfo *uip; int error; uid = uap->uid; newcred = crget(); uip = uifind(uid); PROC_LOCK(p); oldcred = p->p_ucred; /* * See if we have "permission" by POSIX 1003.1 rules. * * Note that setuid(geteuid()) is a special case of * "appropriate privileges" in appendix B.4.2.2. We need * to use this clause to be compatible with traditional BSD * semantics. Basically, it means that "setuid(xx)" sets all * three id's (assuming you have privs). * * Notes on the logic. We do things in three steps. * 1: We determine if the euid is going to change, and do EPERM * right away. We unconditionally change the euid later if this * test is satisfied, simplifying that part of the logic. * 2: We determine if the real and/or saved uids are going to * change. Determined by compile options. * 3: Change euid last. (after tests in #2 for "appropriate privs") */ if (uid != oldcred->cr_ruid && /* allow setuid(getuid()) */ #ifdef _POSIX_SAVED_IDS uid != oldcred->cr_svuid && /* allow setuid(saved gid) */ #endif #ifdef POSIX_APPENDIX_B_4_2_2 /* Use BSD-compat clause from B.4.2.2 */ uid != oldcred->cr_uid && /* allow setuid(geteuid()) */ #endif (error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); uifree(uip); crfree(newcred); return (error); } /* * Copy credentials so other references do not see our changes. */ crcopy(newcred, oldcred); #ifdef _POSIX_SAVED_IDS /* * Do we have "appropriate privileges" (are we root or uid == euid) * If so, we are changing the real uid and/or saved uid. */ if ( #ifdef POSIX_APPENDIX_B_4_2_2 /* Use the clause from B.4.2.2 */ uid == oldcred->cr_uid || #endif suser_cred(oldcred, PRISON_ROOT) == 0) /* we are using privs */ #endif { /* * Set the real uid and transfer proc count to new user. */ if (uid != oldcred->cr_ruid) { change_ruid(newcred, uip); setsugid(p); } /* * Set saved uid * * XXX always set saved uid even if not _POSIX_SAVED_IDS, as * the security of seteuid() depends on it. B.4.2.2 says it * is important that we should do this. */ if (uid != oldcred->cr_svuid) { change_svuid(newcred, uid); setsugid(p); } } /* * In all permitted cases, we are changing the euid. */ if (uid != oldcred->cr_uid) { change_euid(newcred, uip); setsugid(p); } p->p_ucred = newcred; PROC_UNLOCK(p); uifree(uip); crfree(oldcred); return (0); } #ifndef _SYS_SYSPROTO_H_ struct seteuid_args { uid_t euid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int seteuid(struct thread *td, struct seteuid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; uid_t euid; struct uidinfo *euip; int error; euid = uap->euid; newcred = crget(); euip = uifind(euid); PROC_LOCK(p); oldcred = p->p_ucred; if (euid != oldcred->cr_ruid && /* allow seteuid(getuid()) */ euid != oldcred->cr_svuid && /* allow seteuid(saved uid) */ (error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); uifree(euip); crfree(newcred); return (error); } /* * Everything's okay, do it. Copy credentials so other references do * not see our changes. */ crcopy(newcred, oldcred); if (oldcred->cr_uid != euid) { change_euid(newcred, euip); setsugid(p); } p->p_ucred = newcred; PROC_UNLOCK(p); uifree(euip); crfree(oldcred); return (0); } #ifndef _SYS_SYSPROTO_H_ struct setgid_args { gid_t gid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setgid(struct thread *td, struct setgid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; gid_t gid; int error; gid = uap->gid; newcred = crget(); PROC_LOCK(p); oldcred = p->p_ucred; /* * See if we have "permission" by POSIX 1003.1 rules. * * Note that setgid(getegid()) is a special case of * "appropriate privileges" in appendix B.4.2.2. We need * to use this clause to be compatible with traditional BSD * semantics. Basically, it means that "setgid(xx)" sets all * three id's (assuming you have privs). * * For notes on the logic here, see setuid() above. */ if (gid != oldcred->cr_rgid && /* allow setgid(getgid()) */ #ifdef _POSIX_SAVED_IDS gid != oldcred->cr_svgid && /* allow setgid(saved gid) */ #endif #ifdef POSIX_APPENDIX_B_4_2_2 /* Use BSD-compat clause from B.4.2.2 */ gid != oldcred->cr_groups[0] && /* allow setgid(getegid()) */ #endif (error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); crfree(newcred); return (error); } crcopy(newcred, oldcred); #ifdef _POSIX_SAVED_IDS /* * Do we have "appropriate privileges" (are we root or gid == egid) * If so, we are changing the real uid and saved gid. */ if ( #ifdef POSIX_APPENDIX_B_4_2_2 /* use the clause from B.4.2.2 */ gid == oldcred->cr_groups[0] || #endif suser_cred(oldcred, PRISON_ROOT) == 0) /* we are using privs */ #endif { /* * Set real gid */ if (oldcred->cr_rgid != gid) { change_rgid(newcred, gid); setsugid(p); } /* * Set saved gid * * XXX always set saved gid even if not _POSIX_SAVED_IDS, as * the security of setegid() depends on it. B.4.2.2 says it * is important that we should do this. */ if (oldcred->cr_svgid != gid) { change_svgid(newcred, gid); setsugid(p); } } /* * In all cases permitted cases, we are changing the egid. * Copy credentials so other references do not see our changes. */ if (oldcred->cr_groups[0] != gid) { change_egid(newcred, gid); setsugid(p); } p->p_ucred = newcred; PROC_UNLOCK(p); crfree(oldcred); return (0); } #ifndef _SYS_SYSPROTO_H_ struct setegid_args { gid_t egid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setegid(struct thread *td, struct setegid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; gid_t egid; int error; egid = uap->egid; newcred = crget(); PROC_LOCK(p); oldcred = p->p_ucred; if (egid != oldcred->cr_rgid && /* allow setegid(getgid()) */ egid != oldcred->cr_svgid && /* allow setegid(saved gid) */ (error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); crfree(newcred); return (error); } crcopy(newcred, oldcred); if (oldcred->cr_groups[0] != egid) { change_egid(newcred, egid); setsugid(p); } p->p_ucred = newcred; PROC_UNLOCK(p); crfree(oldcred); return (0); } #ifndef _SYS_SYSPROTO_H_ struct setgroups_args { u_int gidsetsize; gid_t *gidset; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setgroups(struct thread *td, struct setgroups_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *tempcred, *oldcred; u_int ngrp; int error; ngrp = uap->gidsetsize; if (ngrp > NGROUPS) return (EINVAL); tempcred = crget(); error = copyin(uap->gidset, tempcred->cr_groups, ngrp * sizeof(gid_t)); if (error != 0) { crfree(tempcred); return (error); } newcred = crget(); PROC_LOCK(p); oldcred = p->p_ucred; error = suser_cred(oldcred, PRISON_ROOT); if (error) { PROC_UNLOCK(p); crfree(newcred); crfree(tempcred); return (error); } /* * XXX A little bit lazy here. We could test if anything has * changed before crcopy() and setting P_SUGID. */ crcopy(newcred, oldcred); if (ngrp < 1) { /* * setgroups(0, NULL) is a legitimate way of clearing the * groups vector on non-BSD systems (which generally do not * have the egid in the groups[0]). We risk security holes * when running non-BSD software if we do not do the same. */ newcred->cr_ngroups = 1; } else { bcopy(tempcred->cr_groups, newcred->cr_groups, ngrp * sizeof(gid_t)); newcred->cr_ngroups = ngrp; } setsugid(p); p->p_ucred = newcred; PROC_UNLOCK(p); crfree(tempcred); crfree(oldcred); return (0); } #ifndef _SYS_SYSPROTO_H_ struct setreuid_args { uid_t ruid; uid_t euid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setreuid(register struct thread *td, struct setreuid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; uid_t euid, ruid; struct uidinfo *euip, *ruip; int error; euid = uap->euid; ruid = uap->ruid; newcred = crget(); euip = uifind(euid); ruip = uifind(ruid); PROC_LOCK(p); oldcred = p->p_ucred; if (((ruid != (uid_t)-1 && ruid != oldcred->cr_ruid && ruid != oldcred->cr_svuid) || (euid != (uid_t)-1 && euid != oldcred->cr_uid && euid != oldcred->cr_ruid && euid != oldcred->cr_svuid)) && (error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); uifree(ruip); uifree(euip); crfree(newcred); return (error); } crcopy(newcred, oldcred); if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { change_euid(newcred, euip); setsugid(p); } if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { change_ruid(newcred, ruip); setsugid(p); } if ((ruid != (uid_t)-1 || newcred->cr_uid != newcred->cr_ruid) && newcred->cr_svuid != newcred->cr_uid) { change_svuid(newcred, newcred->cr_uid); setsugid(p); } p->p_ucred = newcred; PROC_UNLOCK(p); uifree(ruip); uifree(euip); crfree(oldcred); return (0); } #ifndef _SYS_SYSPROTO_H_ struct setregid_args { gid_t rgid; gid_t egid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setregid(register struct thread *td, struct setregid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; gid_t egid, rgid; int error; egid = uap->egid; rgid = uap->rgid; newcred = crget(); PROC_LOCK(p); oldcred = p->p_ucred; if (((rgid != (gid_t)-1 && rgid != oldcred->cr_rgid && rgid != oldcred->cr_svgid) || (egid != (gid_t)-1 && egid != oldcred->cr_groups[0] && egid != oldcred->cr_rgid && egid != oldcred->cr_svgid)) && (error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); crfree(newcred); return (error); } crcopy(newcred, oldcred); if (egid != (gid_t)-1 && oldcred->cr_groups[0] != egid) { change_egid(newcred, egid); setsugid(p); } if (rgid != (gid_t)-1 && oldcred->cr_rgid != rgid) { change_rgid(newcred, rgid); setsugid(p); } if ((rgid != (gid_t)-1 || newcred->cr_groups[0] != newcred->cr_rgid) && newcred->cr_svgid != newcred->cr_groups[0]) { change_svgid(newcred, newcred->cr_groups[0]); setsugid(p); } p->p_ucred = newcred; PROC_UNLOCK(p); crfree(oldcred); return (0); } /* * setresuid(ruid, euid, suid) is like setreuid except control over the * saved uid is explicit. */ #ifndef _SYS_SYSPROTO_H_ struct setresuid_args { uid_t ruid; uid_t euid; uid_t suid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setresuid(register struct thread *td, struct setresuid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; uid_t euid, ruid, suid; struct uidinfo *euip, *ruip; int error; euid = uap->euid; ruid = uap->ruid; suid = uap->suid; newcred = crget(); euip = uifind(euid); ruip = uifind(ruid); PROC_LOCK(p); oldcred = p->p_ucred; if (((ruid != (uid_t)-1 && ruid != oldcred->cr_ruid && ruid != oldcred->cr_svuid && ruid != oldcred->cr_uid) || (euid != (uid_t)-1 && euid != oldcred->cr_ruid && euid != oldcred->cr_svuid && euid != oldcred->cr_uid) || (suid != (uid_t)-1 && suid != oldcred->cr_ruid && suid != oldcred->cr_svuid && suid != oldcred->cr_uid)) && (error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); uifree(ruip); uifree(euip); crfree(newcred); return (error); } crcopy(newcred, oldcred); if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { change_euid(newcred, euip); setsugid(p); } if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { change_ruid(newcred, ruip); setsugid(p); } if (suid != (uid_t)-1 && oldcred->cr_svuid != suid) { change_svuid(newcred, suid); setsugid(p); } p->p_ucred = newcred; PROC_UNLOCK(p); uifree(ruip); uifree(euip); crfree(oldcred); return (0); } /* * setresgid(rgid, egid, sgid) is like setregid except control over the * saved gid is explicit. */ #ifndef _SYS_SYSPROTO_H_ struct setresgid_args { gid_t rgid; gid_t egid; gid_t sgid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setresgid(register struct thread *td, struct setresgid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; gid_t egid, rgid, sgid; int error; egid = uap->egid; rgid = uap->rgid; sgid = uap->sgid; newcred = crget(); PROC_LOCK(p); oldcred = p->p_ucred; if (((rgid != (gid_t)-1 && rgid != oldcred->cr_rgid && rgid != oldcred->cr_svgid && rgid != oldcred->cr_groups[0]) || (egid != (gid_t)-1 && egid != oldcred->cr_rgid && egid != oldcred->cr_svgid && egid != oldcred->cr_groups[0]) || (sgid != (gid_t)-1 && sgid != oldcred->cr_rgid && sgid != oldcred->cr_svgid && sgid != oldcred->cr_groups[0])) && (error = suser_cred(oldcred, PRISON_ROOT)) != 0) { PROC_UNLOCK(p); crfree(newcred); return (error); } crcopy(newcred, oldcred); if (egid != (gid_t)-1 && oldcred->cr_groups[0] != egid) { change_egid(newcred, egid); setsugid(p); } if (rgid != (gid_t)-1 && oldcred->cr_rgid != rgid) { change_rgid(newcred, rgid); setsugid(p); } if (sgid != (gid_t)-1 && oldcred->cr_svgid != sgid) { change_svgid(newcred, sgid); setsugid(p); } p->p_ucred = newcred; PROC_UNLOCK(p); crfree(oldcred); return (0); } #ifndef _SYS_SYSPROTO_H_ struct getresuid_args { uid_t *ruid; uid_t *euid; uid_t *suid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getresuid(register struct thread *td, struct getresuid_args *uap) { struct ucred *cred; int error1 = 0, error2 = 0, error3 = 0; cred = td->td_ucred; if (uap->ruid) error1 = copyout(&cred->cr_ruid, uap->ruid, sizeof(cred->cr_ruid)); if (uap->euid) error2 = copyout(&cred->cr_uid, uap->euid, sizeof(cred->cr_uid)); if (uap->suid) error3 = copyout(&cred->cr_svuid, uap->suid, sizeof(cred->cr_svuid)); return (error1 ? error1 : error2 ? error2 : error3); } #ifndef _SYS_SYSPROTO_H_ struct getresgid_args { gid_t *rgid; gid_t *egid; gid_t *sgid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getresgid(register struct thread *td, struct getresgid_args *uap) { struct ucred *cred; int error1 = 0, error2 = 0, error3 = 0; cred = td->td_ucred; if (uap->rgid) error1 = copyout(&cred->cr_rgid, uap->rgid, sizeof(cred->cr_rgid)); if (uap->egid) error2 = copyout(&cred->cr_groups[0], uap->egid, sizeof(cred->cr_groups[0])); if (uap->sgid) error3 = copyout(&cred->cr_svgid, uap->sgid, sizeof(cred->cr_svgid)); return (error1 ? error1 : error2 ? error2 : error3); } #ifndef _SYS_SYSPROTO_H_ struct issetugid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int issetugid(register struct thread *td, struct issetugid_args *uap) { struct proc *p = td->td_proc; /* * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time, * we use P_SUGID because we consider changing the owners as * "tainting" as well. * This is significant for procs that start as root and "become" * a user without an exec - programs cannot know *everything* * that libc *might* have put in their data segment. */ PROC_LOCK(p); td->td_retval[0] = (p->p_flag & P_SUGID) ? 1 : 0; PROC_UNLOCK(p); return (0); } /* * MPSAFE */ int __setugid(struct thread *td, struct __setugid_args *uap) { #ifdef REGRESSION struct proc *p; p = td->td_proc; switch (uap->flag) { case 0: PROC_LOCK(p); p->p_flag &= ~P_SUGID; PROC_UNLOCK(p); return (0); case 1: PROC_LOCK(p); p->p_flag |= P_SUGID; PROC_UNLOCK(p); return (0); default: return (EINVAL); } #else /* !REGRESSION */ return (ENOSYS); #endif /* REGRESSION */ } /* * Check if gid is a member of the group set. * * MPSAFE (cred must be held) */ int groupmember(gid_t gid, struct ucred *cred) { register gid_t *gp; gid_t *egp; egp = &(cred->cr_groups[cred->cr_ngroups]); for (gp = cred->cr_groups; gp < egp; gp++) if (*gp == gid) return (1); return (0); } /* * `suser_enabled' (which can be set by the security.suser_enabled * sysctl) determines whether the system 'super-user' policy is in effect. * If it is nonzero, an effective uid of 0 connotes special privilege, * overriding many mandatory and discretionary protections. If it is zero, * uid 0 is offered no special privilege in the kernel security policy. * Setting it to zero may seriously impact the functionality of many * existing userland programs, and should not be done without careful * consideration of the consequences. */ int suser_enabled = 1; SYSCTL_INT(_security_bsd, OID_AUTO, suser_enabled, CTLFLAG_RW, &suser_enabled, 0, "processes with uid 0 have privilege"); TUNABLE_INT("security.bsd.suser_enabled", &suser_enabled); /* * Test whether the specified credentials imply "super-user" privilege. * Return 0 or EPERM. The flag argument is currently used only to * specify jail interaction. */ int suser_cred(struct ucred *cred, int flag) { if (!suser_enabled) return (EPERM); if (cred->cr_uid != 0) return (EPERM); if (jailed(cred) && !(flag & PRISON_ROOT)) return (EPERM); return (0); } /* * Shortcut to hide contents of struct td and struct proc from the * caller, promoting binary compatibility. */ int suser(struct thread *td) { return (suser_cred(td->td_ucred, 0)); } /* * Test the active securelevel against a given level. securelevel_gt() * implements (securelevel > level). securelevel_ge() implements * (securelevel >= level). Note that the logic is inverted -- these * functions return EPERM on "success" and 0 on "failure". * * MPSAFE */ int securelevel_gt(struct ucred *cr, int level) { int active_securelevel; active_securelevel = securelevel; KASSERT(cr != NULL, ("securelevel_gt: null cr")); if (cr->cr_prison != NULL) { mtx_lock(&cr->cr_prison->pr_mtx); active_securelevel = imax(cr->cr_prison->pr_securelevel, active_securelevel); mtx_unlock(&cr->cr_prison->pr_mtx); } return (active_securelevel > level ? EPERM : 0); } int securelevel_ge(struct ucred *cr, int level) { int active_securelevel; active_securelevel = securelevel; KASSERT(cr != NULL, ("securelevel_ge: null cr")); if (cr->cr_prison != NULL) { mtx_lock(&cr->cr_prison->pr_mtx); active_securelevel = imax(cr->cr_prison->pr_securelevel, active_securelevel); mtx_unlock(&cr->cr_prison->pr_mtx); } return (active_securelevel >= level ? EPERM : 0); } /* * 'see_other_uids' determines whether or not visibility of processes * and sockets with credentials holding different real uids is possible * using a variety of system MIBs. * XXX: data declarations should be together near the beginning of the file. */ static int see_other_uids = 1; SYSCTL_INT(_security_bsd, OID_AUTO, see_other_uids, CTLFLAG_RW, &see_other_uids, 0, "Unprivileged processes may see subjects/objects with different real uid"); /*- * Determine if u1 "can see" the subject specified by u2, according to the * 'see_other_uids' policy. * Returns: 0 for permitted, ESRCH otherwise * Locks: none * References: *u1 and *u2 must not change during the call * u1 may equal u2, in which case only one reference is required */ static int cr_seeotheruids(struct ucred *u1, struct ucred *u2) { if (!see_other_uids && u1->cr_ruid != u2->cr_ruid) { if (suser_cred(u1, PRISON_ROOT) != 0) return (ESRCH); } return (0); } /* * 'see_other_gids' determines whether or not visibility of processes * and sockets with credentials holding different real gids is possible * using a variety of system MIBs. * XXX: data declarations should be together near the beginning of the file. */ static int see_other_gids = 1; SYSCTL_INT(_security_bsd, OID_AUTO, see_other_gids, CTLFLAG_RW, &see_other_gids, 0, "Unprivileged processes may see subjects/objects with different real gid"); /* * Determine if u1 can "see" the subject specified by u2, according to the * 'see_other_gids' policy. * Returns: 0 for permitted, ESRCH otherwise * Locks: none * References: *u1 and *u2 must not change during the call * u1 may equal u2, in which case only one reference is required */ static int cr_seeothergids(struct ucred *u1, struct ucred *u2) { int i, match; if (!see_other_gids) { match = 0; for (i = 0; i < u1->cr_ngroups; i++) { if (groupmember(u1->cr_groups[i], u2)) match = 1; if (match) break; } if (!match) { if (suser_cred(u1, PRISON_ROOT) != 0) return (ESRCH); } } return (0); } /*- * Determine if u1 "can see" the subject specified by u2. * Returns: 0 for permitted, an errno value otherwise * Locks: none * References: *u1 and *u2 must not change during the call * u1 may equal u2, in which case only one reference is required */ int cr_cansee(struct ucred *u1, struct ucred *u2) { int error; if ((error = prison_check(u1, u2))) return (error); #ifdef MAC if ((error = mac_check_cred_visible(u1, u2))) return (error); #endif if ((error = cr_seeotheruids(u1, u2))) return (error); if ((error = cr_seeothergids(u1, u2))) return (error); return (0); } /*- * Determine if td "can see" the subject specified by p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect p->p_ucred must be held. td really * should be curthread. * References: td and p must be valid for the lifetime of the call */ int p_cansee(struct thread *td, struct proc *p) { /* Wrap cr_cansee() for all functionality. */ KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); return (cr_cansee(td->td_ucred, p->p_ucred)); } /* * 'conservative_signals' prevents the delivery of a broad class of * signals by unprivileged processes to processes that have changed their * credentials since the last invocation of execve(). This can prevent * the leakage of cached information or retained privileges as a result * of a common class of signal-related vulnerabilities. However, this * may interfere with some applications that expect to be able to * deliver these signals to peer processes after having given up * privilege. */ static int conservative_signals = 1; SYSCTL_INT(_security_bsd, OID_AUTO, conservative_signals, CTLFLAG_RW, &conservative_signals, 0, "Unprivileged processes prevented from " "sending certain signals to processes whose credentials have changed"); /*- * Determine whether cred may deliver the specified signal to proc. * Returns: 0 for permitted, an errno value otherwise. * Locks: A lock must be held for proc. * References: cred and proc must be valid for the lifetime of the call. */ int cr_cansignal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); /* * Jail semantics limit the scope of signalling to proc in the * same jail as cred, if cred is in jail. */ error = prison_check(cred, proc->p_ucred); if (error) return (error); #ifdef MAC if ((error = mac_check_proc_signal(cred, proc, signum))) return (error); #endif if ((error = cr_seeotheruids(cred, proc->p_ucred))) return (error); if ((error = cr_seeothergids(cred, proc->p_ucred))) return (error); /* * UNIX signal semantics depend on the status of the P_SUGID * bit on the target process. If the bit is set, then additional * restrictions are placed on the set of available signals. */ if (conservative_signals && (proc->p_flag & P_SUGID)) { switch (signum) { case 0: case SIGKILL: case SIGINT: case SIGTERM: case SIGALRM: case SIGSTOP: case SIGTTIN: case SIGTTOU: case SIGTSTP: case SIGHUP: case SIGUSR1: case SIGUSR2: /* * Generally, permit job and terminal control * signals. */ break; default: /* Not permitted without privilege. */ error = suser_cred(cred, PRISON_ROOT); if (error) return (error); } } /* * Generally, the target credential's ruid or svuid must match the * subject credential's ruid or euid. */ if (cred->cr_ruid != proc->p_ucred->cr_ruid && cred->cr_ruid != proc->p_ucred->cr_svuid && cred->cr_uid != proc->p_ucred->cr_ruid && cred->cr_uid != proc->p_ucred->cr_svuid) { /* Not permitted without privilege. */ error = suser_cred(cred, PRISON_ROOT); if (error) return (error); } return (0); } /*- * Determine whether td may deliver the specified signal to p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect various components of td and p * must be held. td must be curthread, and a lock must be * held for p. * References: td and p must be valid for the lifetime of the call */ int p_cansignal(struct thread *td, struct proc *p, int signum) { KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); if (td->td_proc == p) return (0); /* * UNIX signalling semantics require that processes in the same * session always be able to deliver SIGCONT to one another, * overriding the remaining protections. */ /* XXX: This will require an additional lock of some sort. */ if (signum == SIGCONT && td->td_proc->p_session == p->p_session) return (0); return (cr_cansignal(td->td_ucred, p, signum)); } /*- * Determine whether td may reschedule p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect various components of td and p * must be held. td must be curthread, and a lock must * be held for p. * References: td and p must be valid for the lifetime of the call */ int p_cansched(struct thread *td, struct proc *p) { int error; KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); if (td->td_proc == p) return (0); if ((error = prison_check(td->td_ucred, p->p_ucred))) return (error); #ifdef MAC if ((error = mac_check_proc_sched(td->td_ucred, p))) return (error); #endif if ((error = cr_seeotheruids(td->td_ucred, p->p_ucred))) return (error); if ((error = cr_seeothergids(td->td_ucred, p->p_ucred))) return (error); if (td->td_ucred->cr_ruid == p->p_ucred->cr_ruid) return (0); if (td->td_ucred->cr_uid == p->p_ucred->cr_ruid) return (0); if (suser_cred(td->td_ucred, PRISON_ROOT) == 0) return (0); #ifdef CAPABILITIES if (!cap_check(NULL, td, CAP_SYS_NICE, PRISON_ROOT)) return (0); #endif return (EPERM); } /* * The 'unprivileged_proc_debug' flag may be used to disable a variety of * unprivileged inter-process debugging services, including some procfs * functionality, ptrace(), and ktrace(). In the past, inter-process * debugging has been involved in a variety of security problems, and sites * not requiring the service might choose to disable it when hardening * systems. * * XXX: Should modifying and reading this variable require locking? * XXX: data declarations should be together near the beginning of the file. */ static int unprivileged_proc_debug = 1; SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_proc_debug, CTLFLAG_RW, &unprivileged_proc_debug, 0, "Unprivileged processes may use process debugging facilities"); /*- * Determine whether td may debug p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect various components of td and p * must be held. td must be curthread, and a lock must * be held for p. * References: td and p must be valid for the lifetime of the call */ int p_candebug(struct thread *td, struct proc *p) { int credentialchanged, error, grpsubset, i, uidsubset; KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); if (!unprivileged_proc_debug) { error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); } if (td->td_proc == p) return (0); if ((error = prison_check(td->td_ucred, p->p_ucred))) return (error); #ifdef MAC if ((error = mac_check_proc_debug(td->td_ucred, p))) return (error); #endif if ((error = cr_seeotheruids(td->td_ucred, p->p_ucred))) return (error); if ((error = cr_seeothergids(td->td_ucred, p->p_ucred))) return (error); /* * Is p's group set a subset of td's effective group set? This * includes p's egid, group access list, rgid, and svgid. */ grpsubset = 1; for (i = 0; i < p->p_ucred->cr_ngroups; i++) { if (!groupmember(p->p_ucred->cr_groups[i], td->td_ucred)) { grpsubset = 0; break; } } grpsubset = grpsubset && groupmember(p->p_ucred->cr_rgid, td->td_ucred) && groupmember(p->p_ucred->cr_svgid, td->td_ucred); /* * Are the uids present in p's credential equal to td's * effective uid? This includes p's euid, svuid, and ruid. */ uidsubset = (td->td_ucred->cr_uid == p->p_ucred->cr_uid && td->td_ucred->cr_uid == p->p_ucred->cr_svuid && td->td_ucred->cr_uid == p->p_ucred->cr_ruid); /* * Has the credential of the process changed since the last exec()? */ credentialchanged = (p->p_flag & P_SUGID); /* * If p's gids aren't a subset, or the uids aren't a subset, * or the credential has changed, require appropriate privilege * for td to debug p. For POSIX.1e capabilities, this will * require CAP_SYS_PTRACE. */ if (!grpsubset || !uidsubset || credentialchanged) { error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); } /* Can't trace init when securelevel > 0. */ if (p == initproc) { error = securelevel_gt(td->td_ucred, 0); if (error) return (error); } /* * Can't trace a process that's currently exec'ing. * XXX: Note, this is not a security policy decision, it's a * basic correctness/functionality decision. Therefore, this check * should be moved to the caller's of p_candebug(). */ if ((p->p_flag & P_INEXEC) != 0) return (EAGAIN); return (0); } /*- * Determine whether the subject represented by cred can "see" a socket. * Returns: 0 for permitted, ENOENT otherwise. */ int cr_canseesocket(struct ucred *cred, struct socket *so) { int error; error = prison_check(cred, so->so_cred); if (error) return (ENOENT); #ifdef MAC error = mac_check_socket_visible(cred, so); if (error) return (error); #endif if (cr_seeotheruids(cred, so->so_cred)) return (ENOENT); if (cr_seeothergids(cred, so->so_cred)) return (ENOENT); return (0); } /* * Allocate a zeroed cred structure. * MPSAFE */ struct ucred * crget(void) { register struct ucred *cr; MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK | M_ZERO); cr->cr_ref = 1; cr->cr_mtxp = mtx_pool_find(mtxpool_sleep, cr); #ifdef MAC mac_init_cred(cr); #endif return (cr); } /* * Claim another reference to a ucred structure. * MPSAFE */ struct ucred * crhold(struct ucred *cr) { mtx_lock(cr->cr_mtxp); cr->cr_ref++; mtx_unlock(cr->cr_mtxp); return (cr); } /* * Free a cred structure. * Throws away space when ref count gets to 0. * MPSAFE */ void crfree(struct ucred *cr) { struct mtx *mtxp = cr->cr_mtxp; mtx_lock(mtxp); KASSERT(cr->cr_ref > 0, ("bad ucred refcount: %d", cr->cr_ref)); if (--cr->cr_ref == 0) { mtx_unlock(mtxp); /* * Some callers of crget(), such as nfs_statfs(), * allocate a temporary credential, but don't * allocate a uidinfo structure. */ if (cr->cr_uidinfo != NULL) uifree(cr->cr_uidinfo); if (cr->cr_ruidinfo != NULL) uifree(cr->cr_ruidinfo); /* * Free a prison, if any. */ if (jailed(cr)) prison_free(cr->cr_prison); #ifdef MAC mac_destroy_cred(cr); #endif FREE(cr, M_CRED); } else { mtx_unlock(mtxp); } } /* * Check to see if this ucred is shared. * MPSAFE */ int crshared(struct ucred *cr) { int shared; mtx_lock(cr->cr_mtxp); shared = (cr->cr_ref > 1); mtx_unlock(cr->cr_mtxp); return (shared); } /* * Copy a ucred's contents from a template. Does not block. * MPSAFE */ void crcopy(struct ucred *dest, struct ucred *src) { KASSERT(crshared(dest) == 0, ("crcopy of shared ucred")); bcopy(&src->cr_startcopy, &dest->cr_startcopy, (unsigned)((caddr_t)&src->cr_endcopy - (caddr_t)&src->cr_startcopy)); uihold(dest->cr_uidinfo); uihold(dest->cr_ruidinfo); if (jailed(dest)) prison_hold(dest->cr_prison); #ifdef MAC mac_copy_cred(src, dest); #endif } /* * Dup cred struct to a new held one. * MPSAFE */ struct ucred * crdup(struct ucred *cr) { struct ucred *newcr; newcr = crget(); crcopy(newcr, cr); return (newcr); } #ifdef DIAGNOSTIC void cred_free_thread(struct thread *td) { struct ucred *cred; cred = td->td_ucred; td->td_ucred = NULL; if (cred != NULL) crfree(cred); } #endif /* * Fill in a struct xucred based on a struct ucred. * MPSAFE */ void cru2x(struct ucred *cr, struct xucred *xcr) { bzero(xcr, sizeof(*xcr)); xcr->cr_version = XUCRED_VERSION; xcr->cr_uid = cr->cr_uid; xcr->cr_ngroups = cr->cr_ngroups; bcopy(cr->cr_groups, xcr->cr_groups, sizeof(cr->cr_groups)); } /* * small routine to swap a thread's current ucred for the correct one * taken from the process. * MPSAFE */ void cred_update_thread(struct thread *td) { struct proc *p; struct ucred *cred; p = td->td_proc; cred = td->td_ucred; PROC_LOCK(p); td->td_ucred = crhold(p->p_ucred); PROC_UNLOCK(p); if (cred != NULL) crfree(cred); } /* * Get login name, if available. */ #ifndef _SYS_SYSPROTO_H_ struct getlogin_args { char *namebuf; u_int namelen; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getlogin(struct thread *td, struct getlogin_args *uap) { int error; char login[MAXLOGNAME]; struct proc *p = td->td_proc; if (uap->namelen > MAXLOGNAME) uap->namelen = MAXLOGNAME; PROC_LOCK(p); SESS_LOCK(p->p_session); bcopy(p->p_session->s_login, login, uap->namelen); SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); error = copyout(login, uap->namebuf, uap->namelen); return(error); } /* * Set login name. */ #ifndef _SYS_SYSPROTO_H_ struct setlogin_args { char *namebuf; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setlogin(struct thread *td, struct setlogin_args *uap) { struct proc *p = td->td_proc; int error; char logintmp[MAXLOGNAME]; error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); error = copyinstr(uap->namebuf, logintmp, sizeof(logintmp), NULL); if (error == ENAMETOOLONG) error = EINVAL; else if (!error) { PROC_LOCK(p); SESS_LOCK(p->p_session); (void) memcpy(p->p_session->s_login, logintmp, sizeof(logintmp)); SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); } return (error); } void setsugid(struct proc *p) { PROC_LOCK_ASSERT(p, MA_OWNED); p->p_flag |= P_SUGID; if (!(p->p_pfsflags & PF_ISUGID)) p->p_stops = 0; } /*- * Change a process's effective uid. * Side effects: newcred->cr_uid and newcred->cr_uidinfo will be modified. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_euid(struct ucred *newcred, struct uidinfo *euip) { newcred->cr_uid = euip->ui_uid; uihold(euip); uifree(newcred->cr_uidinfo); newcred->cr_uidinfo = euip; } /*- * Change a process's effective gid. * Side effects: newcred->cr_gid will be modified. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_egid(struct ucred *newcred, gid_t egid) { newcred->cr_groups[0] = egid; } /*- * Change a process's real uid. * Side effects: newcred->cr_ruid will be updated, newcred->cr_ruidinfo * will be updated, and the old and new cr_ruidinfo proc * counts will be updated. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_ruid(struct ucred *newcred, struct uidinfo *ruip) { (void)chgproccnt(newcred->cr_ruidinfo, -1, 0); newcred->cr_ruid = ruip->ui_uid; uihold(ruip); uifree(newcred->cr_ruidinfo); newcred->cr_ruidinfo = ruip; (void)chgproccnt(newcred->cr_ruidinfo, 1, 0); } /*- * Change a process's real gid. * Side effects: newcred->cr_rgid will be updated. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_rgid(struct ucred *newcred, gid_t rgid) { newcred->cr_rgid = rgid; } /*- * Change a process's saved uid. * Side effects: newcred->cr_svuid will be updated. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_svuid(struct ucred *newcred, uid_t svuid) { newcred->cr_svuid = svuid; } /*- * Change a process's saved gid. * Side effects: newcred->cr_svgid will be updated. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_svgid(struct ucred *newcred, gid_t svgid) { newcred->cr_svgid = svgid; } Index: head/sys/kern/kern_resource.c =================================================================== --- head/sys/kern/kern_resource.c (revision 130343) +++ head/sys/kern/kern_resource.c (revision 130344) @@ -1,1156 +1,1156 @@ /*- * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 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. * * @(#)kern_resource.c 8.5 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int donice(struct thread *td, struct proc *chgp, int n); static MALLOC_DEFINE(M_PLIMIT, "plimit", "plimit structures"); static MALLOC_DEFINE(M_UIDINFO, "uidinfo", "uidinfo structures"); #define UIHASH(uid) (&uihashtbl[(uid) & uihash]) static struct mtx uihashtbl_mtx; static LIST_HEAD(uihashhead, uidinfo) *uihashtbl; static u_long uihash; /* size of hash table - 1 */ static struct uidinfo *uilookup(uid_t uid); /* * Resource controls and accounting. */ #ifndef _SYS_SYSPROTO_H_ struct getpriority_args { int which; int who; }; #endif /* * MPSAFE */ int getpriority(td, uap) struct thread *td; register struct getpriority_args *uap; { struct ksegrp *kg; struct proc *p; int error, low; error = 0; low = PRIO_MAX + 1; switch (uap->which) { case PRIO_PROCESS: if (uap->who == 0) low = td->td_ksegrp->kg_nice; else { p = pfind(uap->who); if (p == NULL) break; if (p_cansee(td, p) == 0) { FOREACH_KSEGRP_IN_PROC(p, kg) { if (kg->kg_nice < low) low = kg->kg_nice; } } PROC_UNLOCK(p); } break; case PRIO_PGRP: { register struct pgrp *pg; sx_slock(&proctree_lock); if (uap->who == 0) { pg = td->td_proc->p_pgrp; PGRP_LOCK(pg); } else { pg = pgfind(uap->who); if (pg == NULL) { sx_sunlock(&proctree_lock); break; } } sx_sunlock(&proctree_lock); LIST_FOREACH(p, &pg->pg_members, p_pglist) { PROC_LOCK(p); if (!p_cansee(td, p)) { FOREACH_KSEGRP_IN_PROC(p, kg) { if (kg->kg_nice < low) low = kg->kg_nice; } } PROC_UNLOCK(p); } PGRP_UNLOCK(pg); break; } case PRIO_USER: if (uap->who == 0) uap->who = td->td_ucred->cr_uid; sx_slock(&allproc_lock); LIST_FOREACH(p, &allproc, p_list) { PROC_LOCK(p); if (!p_cansee(td, p) && p->p_ucred->cr_uid == uap->who) { FOREACH_KSEGRP_IN_PROC(p, kg) { if (kg->kg_nice < low) low = kg->kg_nice; } } PROC_UNLOCK(p); } sx_sunlock(&allproc_lock); break; default: error = EINVAL; break; } if (low == PRIO_MAX + 1 && error == 0) error = ESRCH; td->td_retval[0] = low; return (error); } #ifndef _SYS_SYSPROTO_H_ struct setpriority_args { int which; int who; int prio; }; #endif /* * MPSAFE */ int setpriority(td, uap) struct thread *td; register struct setpriority_args *uap; { struct proc *curp; register struct proc *p; int found = 0, error = 0; curp = td->td_proc; switch (uap->which) { case PRIO_PROCESS: if (uap->who == 0) { PROC_LOCK(curp); error = donice(td, curp, uap->prio); PROC_UNLOCK(curp); } else { p = pfind(uap->who); if (p == 0) break; if (p_cansee(td, p) == 0) error = donice(td, p, uap->prio); PROC_UNLOCK(p); } found++; break; case PRIO_PGRP: { register struct pgrp *pg; sx_slock(&proctree_lock); if (uap->who == 0) { pg = curp->p_pgrp; PGRP_LOCK(pg); } else { pg = pgfind(uap->who); if (pg == NULL) { sx_sunlock(&proctree_lock); break; } } sx_sunlock(&proctree_lock); LIST_FOREACH(p, &pg->pg_members, p_pglist) { PROC_LOCK(p); if (!p_cansee(td, p)) { error = donice(td, p, uap->prio); found++; } PROC_UNLOCK(p); } PGRP_UNLOCK(pg); break; } case PRIO_USER: if (uap->who == 0) uap->who = td->td_ucred->cr_uid; sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { PROC_LOCK(p); if (p->p_ucred->cr_uid == uap->who && !p_cansee(td, p)) { error = donice(td, p, uap->prio); found++; } PROC_UNLOCK(p); } sx_sunlock(&allproc_lock); break; default: error = EINVAL; break; } if (found == 0 && error == 0) error = ESRCH; return (error); } /* * Set "nice" for a process. Doesn't really understand threaded processes * well but does try. Has the unfortunate side effect of making all the NICE * values for a process's ksegrps the same. This suggests that * NICE values should be stored as a process nice and deltas for the ksegrps. * (but not yet). */ static int donice(struct thread *td, struct proc *p, int n) { struct ksegrp *kg; int error, low; low = PRIO_MAX + 1; PROC_LOCK_ASSERT(p, MA_OWNED); if ((error = p_cansched(td, p))) return (error); if (n > PRIO_MAX) n = PRIO_MAX; if (n < PRIO_MIN) n = PRIO_MIN; /* * Only allow nicing if to more than the lowest nice. * E.g., for nices of 4,3,2 allow nice to 3 but not 1 */ FOREACH_KSEGRP_IN_PROC(p, kg) { if (kg->kg_nice < low) low = kg->kg_nice; } if (n < low && suser(td) != 0) return (EACCES); mtx_lock_spin(&sched_lock); FOREACH_KSEGRP_IN_PROC(p, kg) { sched_nice(kg, n); } mtx_unlock_spin(&sched_lock); return (0); } /* * Set realtime priority * * MPSAFE */ #ifndef _SYS_SYSPROTO_H_ struct rtprio_args { int function; pid_t pid; struct rtprio *rtp; }; #endif int rtprio(td, uap) struct thread *td; /* curthread */ register struct rtprio_args *uap; { struct proc *curp; struct proc *p; struct ksegrp *kg; struct rtprio rtp; int cierror, error; /* Perform copyin before acquiring locks if needed. */ if (uap->function == RTP_SET) cierror = copyin(uap->rtp, &rtp, sizeof(struct rtprio)); else cierror = 0; curp = td->td_proc; if (uap->pid == 0) { p = curp; PROC_LOCK(p); } else { p = pfind(uap->pid); if (p == NULL) return (ESRCH); } switch (uap->function) { case RTP_LOOKUP: if ((error = p_cansee(td, p))) break; mtx_lock_spin(&sched_lock); /* * Return OUR priority if no pid specified, * or if one is, report the highest priority * in the process. There isn't much more you can do as * there is only room to return a single priority. * XXXKSE Maybe need a new interface to report * priorities of multiple system scope threads. * Note: specifying our own pid is not the same * as leaving it zero. */ if (uap->pid == 0) { pri_to_rtp(td->td_ksegrp, &rtp); } else { struct rtprio rtp2; rtp.type = RTP_PRIO_IDLE; rtp.prio = RTP_PRIO_MAX; FOREACH_KSEGRP_IN_PROC(p, kg) { pri_to_rtp(kg, &rtp2); if ((rtp2.type < rtp.type) || ((rtp2.type == rtp.type) && (rtp2.prio < rtp.prio))) { rtp.type = rtp2.type; rtp.prio = rtp2.prio; } } } mtx_unlock_spin(&sched_lock); PROC_UNLOCK(p); return (copyout(&rtp, uap->rtp, sizeof(struct rtprio))); case RTP_SET: if ((error = p_cansched(td, p)) || (error = cierror)) break; /* disallow setting rtprio in most cases if not superuser */ if (suser(td) != 0) { /* can't set someone else's */ if (uap->pid) { error = EPERM; break; } /* can't set realtime priority */ /* * Realtime priority has to be restricted for reasons which should be * obvious. However, for idle priority, there is a potential for * system deadlock if an idleprio process gains a lock on a resource * that other processes need (and the idleprio process can't run * due to a CPU-bound normal process). Fix me! XXX */ #if 0 if (RTP_PRIO_IS_REALTIME(rtp.type)) #endif if (rtp.type != RTP_PRIO_NORMAL) { error = EPERM; break; } } mtx_lock_spin(&sched_lock); /* * If we are setting our own priority, set just our * KSEGRP but if we are doing another process, * do all the groups on that process. If we * specify our own pid we do the latter. */ if (uap->pid == 0) { error = rtp_to_pri(&rtp, td->td_ksegrp); } else { FOREACH_KSEGRP_IN_PROC(p, kg) { if ((error = rtp_to_pri(&rtp, kg)) != 0) { break; } } } mtx_unlock_spin(&sched_lock); break; default: error = EINVAL; break; } PROC_UNLOCK(p); return (error); } int rtp_to_pri(struct rtprio *rtp, struct ksegrp *kg) { mtx_assert(&sched_lock, MA_OWNED); if (rtp->prio > RTP_PRIO_MAX) return (EINVAL); switch (RTP_PRIO_BASE(rtp->type)) { case RTP_PRIO_REALTIME: kg->kg_user_pri = PRI_MIN_REALTIME + rtp->prio; break; case RTP_PRIO_NORMAL: kg->kg_user_pri = PRI_MIN_TIMESHARE + rtp->prio; break; case RTP_PRIO_IDLE: kg->kg_user_pri = PRI_MIN_IDLE + rtp->prio; break; default: return (EINVAL); } sched_class(kg, rtp->type); if (curthread->td_ksegrp == kg) { curthread->td_base_pri = kg->kg_user_pri; sched_prio(curthread, kg->kg_user_pri); /* XXX dubious */ } return (0); } void pri_to_rtp(struct ksegrp *kg, struct rtprio *rtp) { mtx_assert(&sched_lock, MA_OWNED); switch (PRI_BASE(kg->kg_pri_class)) { case PRI_REALTIME: rtp->prio = kg->kg_user_pri - PRI_MIN_REALTIME; break; case PRI_TIMESHARE: rtp->prio = kg->kg_user_pri - PRI_MIN_TIMESHARE; break; case PRI_IDLE: rtp->prio = kg->kg_user_pri - PRI_MIN_IDLE; break; default: break; } rtp->type = kg->kg_pri_class; } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct osetrlimit_args { u_int which; struct orlimit *rlp; }; #endif /* * MPSAFE */ int osetrlimit(td, uap) struct thread *td; register struct osetrlimit_args *uap; { struct orlimit olim; struct rlimit lim; int error; if ((error = copyin(uap->rlp, &olim, sizeof(struct orlimit)))) return (error); lim.rlim_cur = olim.rlim_cur; lim.rlim_max = olim.rlim_max; error = kern_setrlimit(td, uap->which, &lim); return (error); } #ifndef _SYS_SYSPROTO_H_ struct ogetrlimit_args { u_int which; struct orlimit *rlp; }; #endif /* * MPSAFE */ int ogetrlimit(td, uap) struct thread *td; register struct ogetrlimit_args *uap; { struct orlimit olim; struct rlimit rl; struct proc *p; int error; if (uap->which >= RLIM_NLIMITS) return (EINVAL); p = td->td_proc; PROC_LOCK(p); lim_rlimit(p, uap->which, &rl); PROC_UNLOCK(p); /* * XXX would be more correct to convert only RLIM_INFINITY to the * old RLIM_INFINITY and fail with EOVERFLOW for other larger * values. Most 64->32 and 32->16 conversions, including not * unimportant ones of uids are even more broken than what we * do here (they blindly truncate). We don't do this correctly * here since we have little experience with EOVERFLOW yet. * Elsewhere, getuid() can't fail... */ olim.rlim_cur = rl.rlim_cur > 0x7fffffff ? 0x7fffffff : rl.rlim_cur; olim.rlim_max = rl.rlim_max > 0x7fffffff ? 0x7fffffff : rl.rlim_max; error = copyout(&olim, uap->rlp, sizeof(olim)); return (error); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ #ifndef _SYS_SYSPROTO_H_ struct __setrlimit_args { u_int which; struct rlimit *rlp; }; #endif /* * MPSAFE */ int setrlimit(td, uap) struct thread *td; register struct __setrlimit_args *uap; { struct rlimit alim; int error; if ((error = copyin(uap->rlp, &alim, sizeof(struct rlimit)))) return (error); error = kern_setrlimit(td, uap->which, &alim); return (error); } int kern_setrlimit(td, which, limp) struct thread *td; u_int which; struct rlimit *limp; { struct plimit *newlim, *oldlim; struct proc *p; register struct rlimit *alimp; rlim_t oldssiz; int error; if (which >= RLIM_NLIMITS) return (EINVAL); /* * Preserve historical bugs by treating negative limits as unsigned. */ if (limp->rlim_cur < 0) limp->rlim_cur = RLIM_INFINITY; if (limp->rlim_max < 0) limp->rlim_max = RLIM_INFINITY; oldssiz = 0; p = td->td_proc; newlim = lim_alloc(); PROC_LOCK(p); oldlim = p->p_limit; alimp = &oldlim->pl_rlimit[which]; if (limp->rlim_cur > alimp->rlim_max || limp->rlim_max > alimp->rlim_max) if ((error = suser_cred(td->td_ucred, PRISON_ROOT))) { PROC_UNLOCK(p); lim_free(newlim); return (error); } if (limp->rlim_cur > limp->rlim_max) limp->rlim_cur = limp->rlim_max; lim_copy(newlim, oldlim); alimp = &newlim->pl_rlimit[which]; switch (which) { case RLIMIT_CPU: mtx_lock_spin(&sched_lock); p->p_cpulimit = limp->rlim_cur; mtx_unlock_spin(&sched_lock); break; case RLIMIT_DATA: if (limp->rlim_cur > maxdsiz) limp->rlim_cur = maxdsiz; if (limp->rlim_max > maxdsiz) limp->rlim_max = maxdsiz; break; case RLIMIT_STACK: if (limp->rlim_cur > maxssiz) limp->rlim_cur = maxssiz; if (limp->rlim_max > maxssiz) limp->rlim_max = maxssiz; oldssiz = alimp->rlim_cur; break; case RLIMIT_NOFILE: if (limp->rlim_cur > maxfilesperproc) limp->rlim_cur = maxfilesperproc; if (limp->rlim_max > maxfilesperproc) limp->rlim_max = maxfilesperproc; break; case RLIMIT_NPROC: if (limp->rlim_cur > maxprocperuid) limp->rlim_cur = maxprocperuid; if (limp->rlim_max > maxprocperuid) limp->rlim_max = maxprocperuid; if (limp->rlim_cur < 1) limp->rlim_cur = 1; if (limp->rlim_max < 1) limp->rlim_max = 1; break; } *alimp = *limp; p->p_limit = newlim; PROC_UNLOCK(p); lim_free(oldlim); if (which == RLIMIT_STACK) { /* * Stack is allocated to the max at exec time with only * "rlim_cur" bytes accessible. If stack limit is going * up make more accessible, if going down make inaccessible. */ if (limp->rlim_cur != oldssiz) { vm_offset_t addr; vm_size_t size; vm_prot_t prot; mtx_lock(&Giant); if (limp->rlim_cur > oldssiz) { prot = p->p_sysent->sv_stackprot; size = limp->rlim_cur - oldssiz; addr = p->p_sysent->sv_usrstack - limp->rlim_cur; } else { prot = VM_PROT_NONE; size = oldssiz - limp->rlim_cur; addr = p->p_sysent->sv_usrstack - oldssiz; } addr = trunc_page(addr); size = round_page(size); (void) vm_map_protect(&p->p_vmspace->vm_map, addr, addr+size, prot, FALSE); mtx_unlock(&Giant); } } return (0); } #ifndef _SYS_SYSPROTO_H_ struct __getrlimit_args { u_int which; struct rlimit *rlp; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getrlimit(td, uap) struct thread *td; register struct __getrlimit_args *uap; { struct rlimit rlim; struct proc *p; int error; if (uap->which >= RLIM_NLIMITS) return (EINVAL); p = td->td_proc; PROC_LOCK(p); lim_rlimit(p, uap->which, &rlim); PROC_UNLOCK(p); error = copyout(&rlim, uap->rlp, sizeof(struct rlimit)); return(error); } /* * Transform the running time and tick information in proc p into user, * system, and interrupt time usage. */ void calcru(p, up, sp, ip) struct proc *p; struct timeval *up; struct timeval *sp; struct timeval *ip; { struct bintime bt; struct timeval tv; /* {user, system, interrupt, total} {ticks, usec}; previous tu: */ u_int64_t ut, uu, st, su, it, iu, tt, tu, ptu; mtx_assert(&sched_lock, MA_OWNED); /* XXX: why spl-protect ? worst case is an off-by-one report */ ut = p->p_uticks; st = p->p_sticks; it = p->p_iticks; tt = ut + st + it; if (tt == 0) { st = 1; tt = 1; } if (p == curthread->td_proc) { /* * Adjust for the current time slice. This is actually fairly * important since the error here is on the order of a time * quantum, which is much greater than the sampling error. * XXXKSE use a different test due to threads on other * processors also being 'current'. */ binuptime(&bt); bintime_sub(&bt, PCPU_PTR(switchtime)); bintime_add(&bt, &p->p_runtime); } else bt = p->p_runtime; bintime2timeval(&bt, &tv); tu = (u_int64_t)tv.tv_sec * 1000000 + tv.tv_usec; ptu = p->p_uu + p->p_su + p->p_iu; if (tu < ptu || (int64_t)tu < 0) { printf("calcru: negative time of %jd usec for pid %d (%s)\n", (intmax_t)tu, p->p_pid, p->p_comm); tu = ptu; } /* Subdivide tu. */ uu = (tu * ut) / tt; su = (tu * st) / tt; iu = tu - uu - su; /* Enforce monotonicity. */ if (uu < p->p_uu || su < p->p_su || iu < p->p_iu) { if (uu < p->p_uu) uu = p->p_uu; else if (uu + p->p_su + p->p_iu > tu) uu = tu - p->p_su - p->p_iu; if (st == 0) su = p->p_su; else { su = ((tu - uu) * st) / (st + it); if (su < p->p_su) su = p->p_su; else if (uu + su + p->p_iu > tu) su = tu - uu - p->p_iu; } KASSERT(uu + su + p->p_iu <= tu, ("calcru: monotonisation botch 1")); iu = tu - uu - su; KASSERT(iu >= p->p_iu, ("calcru: monotonisation botch 2")); } p->p_uu = uu; p->p_su = su; p->p_iu = iu; up->tv_sec = uu / 1000000; up->tv_usec = uu % 1000000; sp->tv_sec = su / 1000000; sp->tv_usec = su % 1000000; if (ip != NULL) { ip->tv_sec = iu / 1000000; ip->tv_usec = iu % 1000000; } } #ifndef _SYS_SYSPROTO_H_ struct getrusage_args { int who; struct rusage *rusage; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getrusage(td, uap) register struct thread *td; register struct getrusage_args *uap; { struct rusage ru; struct proc *p; p = td->td_proc; switch (uap->who) { case RUSAGE_SELF: mtx_lock(&Giant); mtx_lock_spin(&sched_lock); calcru(p, &p->p_stats->p_ru.ru_utime, &p->p_stats->p_ru.ru_stime, NULL); mtx_unlock_spin(&sched_lock); ru = p->p_stats->p_ru; mtx_unlock(&Giant); break; case RUSAGE_CHILDREN: mtx_lock(&Giant); ru = p->p_stats->p_cru; mtx_unlock(&Giant); break; default: return (EINVAL); break; } return (copyout(&ru, uap->rusage, sizeof(struct rusage))); } void ruadd(ru, ru2) register struct rusage *ru, *ru2; { register long *ip, *ip2; register int i; timevaladd(&ru->ru_utime, &ru2->ru_utime); timevaladd(&ru->ru_stime, &ru2->ru_stime); if (ru->ru_maxrss < ru2->ru_maxrss) ru->ru_maxrss = ru2->ru_maxrss; ip = &ru->ru_first; ip2 = &ru2->ru_first; for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) *ip++ += *ip2++; } /* * Allocate a new resource limits structure and initialize its * reference count and mutex pointer. */ struct plimit * lim_alloc() { struct plimit *limp; limp = (struct plimit *)malloc(sizeof(struct plimit), M_PLIMIT, M_WAITOK); limp->pl_refcnt = 1; limp->pl_mtx = mtx_pool_alloc(mtxpool_sleep); return (limp); } struct plimit * lim_hold(limp) struct plimit *limp; { LIM_LOCK(limp); limp->pl_refcnt++; LIM_UNLOCK(limp); return (limp); } void lim_free(limp) struct plimit *limp; { LIM_LOCK(limp); KASSERT(limp->pl_refcnt > 0, ("plimit refcnt underflow")); if (--limp->pl_refcnt == 0) { LIM_UNLOCK(limp); free((void *)limp, M_PLIMIT); return; } LIM_UNLOCK(limp); } /* * Make a copy of the plimit structure. * We share these structures copy-on-write after fork. */ void lim_copy(dst, src) struct plimit *dst, *src; { KASSERT(dst->pl_refcnt == 1, ("lim_copy to shared limit")); bcopy(src->pl_rlimit, dst->pl_rlimit, sizeof(src->pl_rlimit)); } /* * Return the hard limit for a particular system resource. The * which parameter specifies the index into the rlimit array. */ rlim_t lim_max(struct proc *p, int which) { struct rlimit rl; lim_rlimit(p, which, &rl); return (rl.rlim_max); } /* * Return the current (soft) limit for a particular system resource. * The which parameter which specifies the index into the rlimit array */ rlim_t lim_cur(struct proc *p, int which) { struct rlimit rl; lim_rlimit(p, which, &rl); return (rl.rlim_cur); } /* * Return a copy of the entire rlimit structure for the system limit * specified by 'which' in the rlimit structure pointed to by 'rlp'. */ void lim_rlimit(struct proc *p, int which, struct rlimit *rlp) { PROC_LOCK_ASSERT(p, MA_OWNED); KASSERT(which >= 0 && which < RLIM_NLIMITS, ("request for invalid resource limit")); *rlp = p->p_limit->pl_rlimit[which]; } /* * Find the uidinfo structure for a uid. This structure is used to * track the total resource consumption (process count, socket buffer * size, etc.) for the uid and impose limits. */ void uihashinit() { uihashtbl = hashinit(maxproc / 16, M_UIDINFO, &uihash); mtx_init(&uihashtbl_mtx, "uidinfo hash", NULL, MTX_DEF); } /* * Look up a uidinfo struct for the parameter uid. * uihashtbl_mtx must be locked. */ static struct uidinfo * uilookup(uid) uid_t uid; { struct uihashhead *uipp; struct uidinfo *uip; mtx_assert(&uihashtbl_mtx, MA_OWNED); uipp = UIHASH(uid); LIST_FOREACH(uip, uipp, ui_hash) if (uip->ui_uid == uid) break; return (uip); } /* * Find or allocate a struct uidinfo for a particular uid. * Increase refcount on uidinfo struct returned. * uifree() should be called on a struct uidinfo when released. */ struct uidinfo * uifind(uid) uid_t uid; { struct uidinfo *old_uip, *uip; mtx_lock(&uihashtbl_mtx); uip = uilookup(uid); if (uip == NULL) { mtx_unlock(&uihashtbl_mtx); uip = malloc(sizeof(*uip), M_UIDINFO, M_WAITOK | M_ZERO); mtx_lock(&uihashtbl_mtx); /* * There's a chance someone created our uidinfo while we * were in malloc and not holding the lock, so we have to * make sure we don't insert a duplicate uidinfo. */ if ((old_uip = uilookup(uid)) != NULL) { /* Someone else beat us to it. */ free(uip, M_UIDINFO); uip = old_uip; } else { uip->ui_mtxp = mtx_pool_alloc(mtxpool_sleep); uip->ui_uid = uid; LIST_INSERT_HEAD(UIHASH(uid), uip, ui_hash); } } uihold(uip); mtx_unlock(&uihashtbl_mtx); return (uip); } /* * Place another refcount on a uidinfo struct. */ void uihold(uip) struct uidinfo *uip; { UIDINFO_LOCK(uip); uip->ui_ref++; UIDINFO_UNLOCK(uip); } /*- * Since uidinfo structs have a long lifetime, we use an * opportunistic refcounting scheme to avoid locking the lookup hash * for each release. * * If the refcount hits 0, we need to free the structure, * which means we need to lock the hash. * Optimal case: * After locking the struct and lowering the refcount, if we find * that we don't need to free, simply unlock and return. * Suboptimal case: * If refcount lowering results in need to free, bump the count * back up, loose the lock and aquire the locks in the proper * order to try again. */ void uifree(uip) struct uidinfo *uip; { /* Prepare for optimal case. */ UIDINFO_LOCK(uip); if (--uip->ui_ref != 0) { UIDINFO_UNLOCK(uip); return; } /* Prepare for suboptimal case. */ uip->ui_ref++; UIDINFO_UNLOCK(uip); mtx_lock(&uihashtbl_mtx); UIDINFO_LOCK(uip); /* * We must subtract one from the count again because we backed out * our initial subtraction before dropping the lock. * Since another thread may have added a reference after we dropped the * initial lock we have to test for zero again. */ if (--uip->ui_ref == 0) { LIST_REMOVE(uip, ui_hash); mtx_unlock(&uihashtbl_mtx); if (uip->ui_sbsize != 0) printf("freeing uidinfo: uid = %d, sbsize = %jd\n", uip->ui_uid, (intmax_t)uip->ui_sbsize); if (uip->ui_proccnt != 0) printf("freeing uidinfo: uid = %d, proccnt = %ld\n", uip->ui_uid, uip->ui_proccnt); UIDINFO_UNLOCK(uip); FREE(uip, M_UIDINFO); return; } mtx_unlock(&uihashtbl_mtx); UIDINFO_UNLOCK(uip); } /* * Change the count associated with number of processes * a given user is using. When 'max' is 0, don't enforce a limit */ int chgproccnt(uip, diff, max) struct uidinfo *uip; int diff; int max; { UIDINFO_LOCK(uip); /* Don't allow them to exceed max, but allow subtraction. */ if (diff > 0 && uip->ui_proccnt + diff > max && max != 0) { UIDINFO_UNLOCK(uip); return (0); } uip->ui_proccnt += diff; if (uip->ui_proccnt < 0) printf("negative proccnt for uid = %d\n", uip->ui_uid); UIDINFO_UNLOCK(uip); return (1); } /* * Change the total socket buffer size a user has used. */ int chgsbsize(uip, hiwat, to, max) struct uidinfo *uip; u_int *hiwat; u_int to; rlim_t max; { rlim_t new; int s; s = splnet(); UIDINFO_LOCK(uip); new = uip->ui_sbsize + to - *hiwat; /* Don't allow them to exceed max, but allow subtraction */ if (to > *hiwat && new > max) { splx(s); UIDINFO_UNLOCK(uip); return (0); } uip->ui_sbsize = new; *hiwat = to; if (uip->ui_sbsize < 0) printf("negative sbsize for uid = %d\n", uip->ui_uid); splx(s); UIDINFO_UNLOCK(uip); return (1); } Index: head/sys/kern/kern_sig.c =================================================================== --- head/sys/kern/kern_sig.c (revision 130343) +++ head/sys/kern/kern_sig.c (revision 130344) @@ -1,2778 +1,2766 @@ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 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. * * @(#)kern_sig.c 8.7 (Berkeley) 4/18/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined (__alpha__) && !defined(COMPAT_43) #error "You *really* need COMPAT_43 on the alpha for longjmp(3)" #endif #define ONSIG 32 /* NSIG for osig* syscalls. XXX. */ static int coredump(struct thread *); static char *expand_name(const char *, uid_t, pid_t); static int killpg1(struct thread *td, int sig, int pgid, int all); static int issignal(struct thread *p); static int sigprop(int sig); static void stop(struct proc *); static void tdsigwakeup(struct thread *td, int sig, sig_t action); static int filt_sigattach(struct knote *kn); static void filt_sigdetach(struct knote *kn); static int filt_signal(struct knote *kn, long hint); static struct thread *sigtd(struct proc *p, int sig, int prop); static int kern_sigtimedwait(struct thread *td, sigset_t set, siginfo_t *info, struct timespec *timeout); static void do_tdsignal(struct thread *td, int sig, sigtarget_t target); struct filterops sig_filtops = { 0, filt_sigattach, filt_sigdetach, filt_signal }; static int kern_logsigexit = 1; SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW, &kern_logsigexit, 0, "Log processes quitting on abnormal signals to syslog(3)"); /* * Policy -- Can ucred cr1 send SIGIO to process cr2? * Should use cr_cansignal() once cr_cansignal() allows SIGIO and SIGURG * in the right situations. */ #define CANSIGIO(cr1, cr2) \ ((cr1)->cr_uid == 0 || \ (cr1)->cr_ruid == (cr2)->cr_ruid || \ (cr1)->cr_uid == (cr2)->cr_ruid || \ (cr1)->cr_ruid == (cr2)->cr_uid || \ (cr1)->cr_uid == (cr2)->cr_uid) int sugid_coredump; SYSCTL_INT(_kern, OID_AUTO, sugid_coredump, CTLFLAG_RW, &sugid_coredump, 0, "Enable coredumping set user/group ID processes"); static int do_coredump = 1; SYSCTL_INT(_kern, OID_AUTO, coredump, CTLFLAG_RW, &do_coredump, 0, "Enable/Disable coredumps"); /* * Signal properties and actions. * The array below categorizes the signals and their default actions * according to the following properties: */ #define SA_KILL 0x01 /* terminates process by default */ #define SA_CORE 0x02 /* ditto and coredumps */ #define SA_STOP 0x04 /* suspend process */ #define SA_TTYSTOP 0x08 /* ditto, from tty */ #define SA_IGNORE 0x10 /* ignore by default */ #define SA_CONT 0x20 /* continue if suspended */ #define SA_CANTMASK 0x40 /* non-maskable, catchable */ #define SA_PROC 0x80 /* deliverable to any thread */ static int sigproptbl[NSIG] = { SA_KILL|SA_PROC, /* SIGHUP */ SA_KILL|SA_PROC, /* SIGINT */ SA_KILL|SA_CORE|SA_PROC, /* SIGQUIT */ SA_KILL|SA_CORE, /* SIGILL */ SA_KILL|SA_CORE, /* SIGTRAP */ SA_KILL|SA_CORE, /* SIGABRT */ SA_KILL|SA_CORE|SA_PROC, /* SIGEMT */ SA_KILL|SA_CORE, /* SIGFPE */ SA_KILL|SA_PROC, /* SIGKILL */ SA_KILL|SA_CORE, /* SIGBUS */ SA_KILL|SA_CORE, /* SIGSEGV */ SA_KILL|SA_CORE, /* SIGSYS */ SA_KILL|SA_PROC, /* SIGPIPE */ SA_KILL|SA_PROC, /* SIGALRM */ SA_KILL|SA_PROC, /* SIGTERM */ SA_IGNORE|SA_PROC, /* SIGURG */ SA_STOP|SA_PROC, /* SIGSTOP */ SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTSTP */ SA_IGNORE|SA_CONT|SA_PROC, /* SIGCONT */ SA_IGNORE|SA_PROC, /* SIGCHLD */ SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTTIN */ SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTTOU */ SA_IGNORE|SA_PROC, /* SIGIO */ SA_KILL, /* SIGXCPU */ SA_KILL, /* SIGXFSZ */ SA_KILL|SA_PROC, /* SIGVTALRM */ SA_KILL|SA_PROC, /* SIGPROF */ SA_IGNORE|SA_PROC, /* SIGWINCH */ SA_IGNORE|SA_PROC, /* SIGINFO */ SA_KILL|SA_PROC, /* SIGUSR1 */ SA_KILL|SA_PROC, /* SIGUSR2 */ }; /* * Determine signal that should be delivered to process p, the current * process, 0 if none. If there is a pending stop signal with default * action, the process stops in issignal(). * XXXKSE the check for a pending stop is not done under KSE * * MP SAFE. */ int cursig(struct thread *td) { PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); mtx_assert(&td->td_proc->p_sigacts->ps_mtx, MA_OWNED); mtx_assert(&sched_lock, MA_NOTOWNED); return (SIGPENDING(td) ? issignal(td) : 0); } /* * Arrange for ast() to handle unmasked pending signals on return to user * mode. This must be called whenever a signal is added to td_siglist or * unmasked in td_sigmask. */ void signotify(struct thread *td) { struct proc *p; sigset_t set, saved; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); /* * If our mask changed we may have to move signal that were * previously masked by all threads to our siglist. */ set = p->p_siglist; if (p->p_flag & P_SA) saved = p->p_siglist; SIGSETNAND(set, td->td_sigmask); SIGSETNAND(p->p_siglist, set); SIGSETOR(td->td_siglist, set); if (SIGPENDING(td)) { mtx_lock_spin(&sched_lock); td->td_flags |= TDF_NEEDSIGCHK | TDF_ASTPENDING; mtx_unlock_spin(&sched_lock); } if ((p->p_flag & P_SA) && !(p->p_flag & P_SIGEVENT)) { if (SIGSETEQ(saved, p->p_siglist)) return; else { /* pending set changed */ p->p_flag |= P_SIGEVENT; wakeup(&p->p_siglist); } } } int sigonstack(size_t sp) { struct thread *td = curthread; return ((td->td_pflags & TDP_ALTSTACK) ? -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) ((td->td_sigstk.ss_size == 0) ? (td->td_sigstk.ss_flags & SS_ONSTACK) : ((sp - (size_t)td->td_sigstk.ss_sp) < td->td_sigstk.ss_size)) #else ((sp - (size_t)td->td_sigstk.ss_sp) < td->td_sigstk.ss_size) #endif : 0); } static __inline int sigprop(int sig) { if (sig > 0 && sig < NSIG) return (sigproptbl[_SIG_IDX(sig)]); return (0); } int sig_ffs(sigset_t *set) { int i; for (i = 0; i < _SIG_WORDS; i++) if (set->__bits[i]) return (ffs(set->__bits[i]) + (i * 32)); return (0); } /* * kern_sigaction * sigaction * freebsd4_sigaction * osigaction * * MPSAFE */ int kern_sigaction(td, sig, act, oact, flags) struct thread *td; register int sig; struct sigaction *act, *oact; int flags; { struct sigacts *ps; struct thread *td0; struct proc *p = td->td_proc; if (!_SIG_VALID(sig)) return (EINVAL); PROC_LOCK(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); if (oact) { oact->sa_handler = ps->ps_sigact[_SIG_IDX(sig)]; oact->sa_mask = ps->ps_catchmask[_SIG_IDX(sig)]; oact->sa_flags = 0; if (SIGISMEMBER(ps->ps_sigonstack, sig)) oact->sa_flags |= SA_ONSTACK; if (!SIGISMEMBER(ps->ps_sigintr, sig)) oact->sa_flags |= SA_RESTART; if (SIGISMEMBER(ps->ps_sigreset, sig)) oact->sa_flags |= SA_RESETHAND; if (SIGISMEMBER(ps->ps_signodefer, sig)) oact->sa_flags |= SA_NODEFER; if (SIGISMEMBER(ps->ps_siginfo, sig)) oact->sa_flags |= SA_SIGINFO; if (sig == SIGCHLD && ps->ps_flag & PS_NOCLDSTOP) oact->sa_flags |= SA_NOCLDSTOP; if (sig == SIGCHLD && ps->ps_flag & PS_NOCLDWAIT) oact->sa_flags |= SA_NOCLDWAIT; } if (act) { if ((sig == SIGKILL || sig == SIGSTOP) && act->sa_handler != SIG_DFL) { mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); return (EINVAL); } /* * Change setting atomically. */ ps->ps_catchmask[_SIG_IDX(sig)] = act->sa_mask; SIG_CANTMASK(ps->ps_catchmask[_SIG_IDX(sig)]); if (act->sa_flags & SA_SIGINFO) { ps->ps_sigact[_SIG_IDX(sig)] = (__sighandler_t *)act->sa_sigaction; SIGADDSET(ps->ps_siginfo, sig); } else { ps->ps_sigact[_SIG_IDX(sig)] = act->sa_handler; SIGDELSET(ps->ps_siginfo, sig); } if (!(act->sa_flags & SA_RESTART)) SIGADDSET(ps->ps_sigintr, sig); else SIGDELSET(ps->ps_sigintr, sig); if (act->sa_flags & SA_ONSTACK) SIGADDSET(ps->ps_sigonstack, sig); else SIGDELSET(ps->ps_sigonstack, sig); if (act->sa_flags & SA_RESETHAND) SIGADDSET(ps->ps_sigreset, sig); else SIGDELSET(ps->ps_sigreset, sig); if (act->sa_flags & SA_NODEFER) SIGADDSET(ps->ps_signodefer, sig); else SIGDELSET(ps->ps_signodefer, sig); -#ifdef COMPAT_SUNOS - if (act->sa_flags & SA_USERTRAMP) - SIGADDSET(ps->ps_usertramp, sig); - else - SIGDELSET(ps->ps_usertramp, sig); -#endif if (sig == SIGCHLD) { if (act->sa_flags & SA_NOCLDSTOP) ps->ps_flag |= PS_NOCLDSTOP; else ps->ps_flag &= ~PS_NOCLDSTOP; if (act->sa_flags & SA_NOCLDWAIT) { /* * Paranoia: since SA_NOCLDWAIT is implemented * by reparenting the dying child to PID 1 (and * trust it to reap the zombie), PID 1 itself * is forbidden to set SA_NOCLDWAIT. */ if (p->p_pid == 1) ps->ps_flag &= ~PS_NOCLDWAIT; else ps->ps_flag |= PS_NOCLDWAIT; } else ps->ps_flag &= ~PS_NOCLDWAIT; if (ps->ps_sigact[_SIG_IDX(SIGCHLD)] == SIG_IGN) ps->ps_flag |= PS_CLDSIGIGN; else ps->ps_flag &= ~PS_CLDSIGIGN; } /* * Set bit in ps_sigignore for signals that are set to SIG_IGN, * and for signals set to SIG_DFL where the default is to * ignore. However, don't put SIGCONT in ps_sigignore, as we * have to restart the process. */ if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || (sigprop(sig) & SA_IGNORE && ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL)) { if ((p->p_flag & P_SA) && SIGISMEMBER(p->p_siglist, sig)) { p->p_flag |= P_SIGEVENT; wakeup(&p->p_siglist); } /* never to be seen again */ SIGDELSET(p->p_siglist, sig); mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) SIGDELSET(td0->td_siglist, sig); mtx_unlock_spin(&sched_lock); if (sig != SIGCONT) /* easier in psignal */ SIGADDSET(ps->ps_sigignore, sig); SIGDELSET(ps->ps_sigcatch, sig); } else { SIGDELSET(ps->ps_sigignore, sig); if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL) SIGDELSET(ps->ps_sigcatch, sig); else SIGADDSET(ps->ps_sigcatch, sig); } #ifdef COMPAT_FREEBSD4 if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL || (flags & KSA_FREEBSD4) == 0) SIGDELSET(ps->ps_freebsd4, sig); else SIGADDSET(ps->ps_freebsd4, sig); #endif #ifdef COMPAT_43 if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL || (flags & KSA_OSIGSET) == 0) SIGDELSET(ps->ps_osigset, sig); else SIGADDSET(ps->ps_osigset, sig); #endif } mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); return (0); } #ifndef _SYS_SYSPROTO_H_ struct sigaction_args { int sig; struct sigaction *act; struct sigaction *oact; }; #endif /* * MPSAFE */ int sigaction(td, uap) struct thread *td; register struct sigaction_args *uap; { struct sigaction act, oact; register struct sigaction *actp, *oactp; int error; actp = (uap->act != NULL) ? &act : NULL; oactp = (uap->oact != NULL) ? &oact : NULL; if (actp) { error = copyin(uap->act, actp, sizeof(act)); if (error) return (error); } error = kern_sigaction(td, uap->sig, actp, oactp, 0); if (oactp && !error) error = copyout(oactp, uap->oact, sizeof(oact)); return (error); } #ifdef COMPAT_FREEBSD4 #ifndef _SYS_SYSPROTO_H_ struct freebsd4_sigaction_args { int sig; struct sigaction *act; struct sigaction *oact; }; #endif /* * MPSAFE */ int freebsd4_sigaction(td, uap) struct thread *td; register struct freebsd4_sigaction_args *uap; { struct sigaction act, oact; register struct sigaction *actp, *oactp; int error; actp = (uap->act != NULL) ? &act : NULL; oactp = (uap->oact != NULL) ? &oact : NULL; if (actp) { error = copyin(uap->act, actp, sizeof(act)); if (error) return (error); } error = kern_sigaction(td, uap->sig, actp, oactp, KSA_FREEBSD4); if (oactp && !error) error = copyout(oactp, uap->oact, sizeof(oact)); return (error); } #endif /* COMAPT_FREEBSD4 */ #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ #ifndef _SYS_SYSPROTO_H_ struct osigaction_args { int signum; struct osigaction *nsa; struct osigaction *osa; }; #endif /* * MPSAFE */ int osigaction(td, uap) struct thread *td; register struct osigaction_args *uap; { struct osigaction sa; struct sigaction nsa, osa; register struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsa != NULL) ? &nsa : NULL; osap = (uap->osa != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsa, &sa, sizeof(sa)); if (error) return (error); nsap->sa_handler = sa.sa_handler; nsap->sa_flags = sa.sa_flags; OSIG2SIG(sa.sa_mask, nsap->sa_mask); } error = kern_sigaction(td, uap->signum, nsap, osap, KSA_OSIGSET); if (osap && !error) { sa.sa_handler = osap->sa_handler; sa.sa_flags = osap->sa_flags; SIG2OSIG(osap->sa_mask, sa.sa_mask); error = copyout(&sa, uap->osa, sizeof(sa)); } return (error); } #if !defined(__i386__) && !defined(__alpha__) /* Avoid replicating the same stub everywhere */ int osigreturn(td, uap) struct thread *td; struct osigreturn_args *uap; { return (nosys(td, (struct nosys_args *)uap)); } #endif #endif /* COMPAT_43 */ /* * Initialize signal state for process 0; * set to ignore signals that are ignored by default. */ void siginit(p) struct proc *p; { register int i; struct sigacts *ps; PROC_LOCK(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); for (i = 1; i <= NSIG; i++) if (sigprop(i) & SA_IGNORE && i != SIGCONT) SIGADDSET(ps->ps_sigignore, i); mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); } /* * Reset signals for an exec of the specified process. */ void execsigs(struct proc *p) { struct sigacts *ps; int sig; struct thread *td; /* * Reset caught signals. Held signals remain held * through td_sigmask (unless they were caught, * and are now ignored by default). */ PROC_LOCK_ASSERT(p, MA_OWNED); td = FIRST_THREAD_IN_PROC(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); while (SIGNOTEMPTY(ps->ps_sigcatch)) { sig = sig_ffs(&ps->ps_sigcatch); SIGDELSET(ps->ps_sigcatch, sig); if (sigprop(sig) & SA_IGNORE) { if (sig != SIGCONT) SIGADDSET(ps->ps_sigignore, sig); SIGDELSET(p->p_siglist, sig); /* * There is only one thread at this point. */ SIGDELSET(td->td_siglist, sig); } ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } /* * Reset stack state to the user stack. * Clear set of signals caught on the signal stack. */ td->td_sigstk.ss_flags = SS_DISABLE; td->td_sigstk.ss_size = 0; td->td_sigstk.ss_sp = 0; td->td_pflags &= ~TDP_ALTSTACK; /* * Reset no zombies if child dies flag as Solaris does. */ ps->ps_flag &= ~(PS_NOCLDWAIT | PS_CLDSIGIGN); if (ps->ps_sigact[_SIG_IDX(SIGCHLD)] == SIG_IGN) ps->ps_sigact[_SIG_IDX(SIGCHLD)] = SIG_DFL; mtx_unlock(&ps->ps_mtx); } /* * kern_sigprocmask() * * Manipulate signal mask. */ int kern_sigprocmask(td, how, set, oset, old) struct thread *td; int how; sigset_t *set, *oset; int old; { int error; PROC_LOCK(td->td_proc); if (oset != NULL) *oset = td->td_sigmask; error = 0; if (set != NULL) { switch (how) { case SIG_BLOCK: SIG_CANTMASK(*set); SIGSETOR(td->td_sigmask, *set); break; case SIG_UNBLOCK: SIGSETNAND(td->td_sigmask, *set); signotify(td); break; case SIG_SETMASK: SIG_CANTMASK(*set); if (old) SIGSETLO(td->td_sigmask, *set); else td->td_sigmask = *set; signotify(td); break; default: error = EINVAL; break; } } PROC_UNLOCK(td->td_proc); return (error); } /* * sigprocmask() - MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct sigprocmask_args { int how; const sigset_t *set; sigset_t *oset; }; #endif int sigprocmask(td, uap) register struct thread *td; struct sigprocmask_args *uap; { sigset_t set, oset; sigset_t *setp, *osetp; int error; setp = (uap->set != NULL) ? &set : NULL; osetp = (uap->oset != NULL) ? &oset : NULL; if (setp) { error = copyin(uap->set, setp, sizeof(set)); if (error) return (error); } error = kern_sigprocmask(td, uap->how, setp, osetp, 0); if (osetp && !error) { error = copyout(osetp, uap->oset, sizeof(oset)); } return (error); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ /* * osigprocmask() - MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct osigprocmask_args { int how; osigset_t mask; }; #endif int osigprocmask(td, uap) register struct thread *td; struct osigprocmask_args *uap; { sigset_t set, oset; int error; OSIG2SIG(uap->mask, set); error = kern_sigprocmask(td, uap->how, &set, &oset, 1); SIG2OSIG(oset, td->td_retval[0]); return (error); } #endif /* COMPAT_43 */ #ifndef _SYS_SYSPROTO_H_ struct sigpending_args { sigset_t *set; }; #endif /* * MPSAFE */ int sigwait(struct thread *td, struct sigwait_args *uap) { siginfo_t info; sigset_t set; int error; error = copyin(uap->set, &set, sizeof(set)); if (error) { td->td_retval[0] = error; return (0); } error = kern_sigtimedwait(td, set, &info, NULL); if (error) { if (error == ERESTART) return (error); td->td_retval[0] = error; return (0); } error = copyout(&info.si_signo, uap->sig, sizeof(info.si_signo)); /* Repost if we got an error. */ if (error && info.si_signo) { PROC_LOCK(td->td_proc); tdsignal(td, info.si_signo, SIGTARGET_TD); PROC_UNLOCK(td->td_proc); } td->td_retval[0] = error; return (0); } /* * MPSAFE */ int sigtimedwait(struct thread *td, struct sigtimedwait_args *uap) { struct timespec ts; struct timespec *timeout; sigset_t set; siginfo_t info; int error; if (uap->timeout) { error = copyin(uap->timeout, &ts, sizeof(ts)); if (error) return (error); timeout = &ts; } else timeout = NULL; error = copyin(uap->set, &set, sizeof(set)); if (error) return (error); error = kern_sigtimedwait(td, set, &info, timeout); if (error) return (error); if (uap->info) error = copyout(&info, uap->info, sizeof(info)); /* Repost if we got an error. */ if (error && info.si_signo) { PROC_LOCK(td->td_proc); tdsignal(td, info.si_signo, SIGTARGET_TD); PROC_UNLOCK(td->td_proc); } else { td->td_retval[0] = info.si_signo; } return (error); } /* * MPSAFE */ int sigwaitinfo(struct thread *td, struct sigwaitinfo_args *uap) { siginfo_t info; sigset_t set; int error; error = copyin(uap->set, &set, sizeof(set)); if (error) return (error); error = kern_sigtimedwait(td, set, &info, NULL); if (error) return (error); if (uap->info) error = copyout(&info, uap->info, sizeof(info)); /* Repost if we got an error. */ if (error && info.si_signo) { PROC_LOCK(td->td_proc); tdsignal(td, info.si_signo, SIGTARGET_TD); PROC_UNLOCK(td->td_proc); } else { td->td_retval[0] = info.si_signo; } return (error); } static int kern_sigtimedwait(struct thread *td, sigset_t waitset, siginfo_t *info, struct timespec *timeout) { struct sigacts *ps; sigset_t savedmask, sigset; struct proc *p; int error; int sig; int hz; int i; p = td->td_proc; error = 0; sig = 0; SIG_CANTMASK(waitset); PROC_LOCK(p); ps = p->p_sigacts; savedmask = td->td_sigmask; again: for (i = 1; i <= _SIG_MAXSIG; ++i) { if (!SIGISMEMBER(waitset, i)) continue; if (SIGISMEMBER(td->td_siglist, i)) { SIGFILLSET(td->td_sigmask); SIG_CANTMASK(td->td_sigmask); SIGDELSET(td->td_sigmask, i); mtx_lock(&ps->ps_mtx); sig = cursig(td); i = 0; mtx_unlock(&ps->ps_mtx); } else if (SIGISMEMBER(p->p_siglist, i)) { if (p->p_flag & P_SA) { p->p_flag |= P_SIGEVENT; wakeup(&p->p_siglist); } SIGDELSET(p->p_siglist, i); SIGADDSET(td->td_siglist, i); SIGFILLSET(td->td_sigmask); SIG_CANTMASK(td->td_sigmask); SIGDELSET(td->td_sigmask, i); mtx_lock(&ps->ps_mtx); sig = cursig(td); i = 0; mtx_unlock(&ps->ps_mtx); } if (sig) { td->td_sigmask = savedmask; signotify(td); goto out; } } if (error) goto out; td->td_sigmask = savedmask; signotify(td); sigset = td->td_siglist; SIGSETOR(sigset, p->p_siglist); SIGSETAND(sigset, waitset); if (!SIGISEMPTY(sigset)) goto again; /* * POSIX says this must be checked after looking for pending * signals. */ if (timeout) { struct timeval tv; if (timeout->tv_nsec < 0 || timeout->tv_nsec > 1000000000) { error = EINVAL; goto out; } if (timeout->tv_sec == 0 && timeout->tv_nsec == 0) { error = EAGAIN; goto out; } TIMESPEC_TO_TIMEVAL(&tv, timeout); hz = tvtohz(&tv); } else hz = 0; td->td_waitset = &waitset; error = msleep(&ps, &p->p_mtx, PPAUSE|PCATCH, "sigwait", hz); td->td_waitset = NULL; if (error == 0) /* surplus wakeup ? */ error = EINTR; goto again; out: if (sig) { sig_t action; error = 0; mtx_lock(&ps->ps_mtx); action = ps->ps_sigact[_SIG_IDX(sig)]; mtx_unlock(&ps->ps_mtx); #ifdef KTRACE if (KTRPOINT(td, KTR_PSIG)) ktrpsig(sig, action, &td->td_sigmask, 0); #endif _STOPEVENT(p, S_SIG, sig); SIGDELSET(td->td_siglist, sig); info->si_signo = sig; info->si_code = 0; } PROC_UNLOCK(p); return (error); } /* * MPSAFE */ int sigpending(td, uap) struct thread *td; struct sigpending_args *uap; { struct proc *p = td->td_proc; sigset_t siglist; PROC_LOCK(p); siglist = p->p_siglist; SIGSETOR(siglist, td->td_siglist); PROC_UNLOCK(p); return (copyout(&siglist, uap->set, sizeof(sigset_t))); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ #ifndef _SYS_SYSPROTO_H_ struct osigpending_args { int dummy; }; #endif /* * MPSAFE */ int osigpending(td, uap) struct thread *td; struct osigpending_args *uap; { struct proc *p = td->td_proc; sigset_t siglist; PROC_LOCK(p); siglist = p->p_siglist; SIGSETOR(siglist, td->td_siglist); PROC_UNLOCK(p); SIG2OSIG(siglist, td->td_retval[0]); return (0); } #endif /* COMPAT_43 */ -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Generalized interface signal handler, 4.3-compatible. */ #ifndef _SYS_SYSPROTO_H_ struct osigvec_args { int signum; struct sigvec *nsv; struct sigvec *osv; }; #endif /* * MPSAFE */ /* ARGSUSED */ int osigvec(td, uap) struct thread *td; register struct osigvec_args *uap; { struct sigvec vec; struct sigaction nsa, osa; register struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsv != NULL) ? &nsa : NULL; osap = (uap->osv != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsv, &vec, sizeof(vec)); if (error) return (error); nsap->sa_handler = vec.sv_handler; OSIG2SIG(vec.sv_mask, nsap->sa_mask); nsap->sa_flags = vec.sv_flags; nsap->sa_flags ^= SA_RESTART; /* opposite of SV_INTERRUPT */ -#ifdef COMPAT_SUNOS - nsap->sa_flags |= SA_USERTRAMP; -#endif } error = kern_sigaction(td, uap->signum, nsap, osap, KSA_OSIGSET); if (osap && !error) { vec.sv_handler = osap->sa_handler; SIG2OSIG(osap->sa_mask, vec.sv_mask); vec.sv_flags = osap->sa_flags; vec.sv_flags &= ~SA_NOCLDWAIT; vec.sv_flags ^= SA_RESTART; -#ifdef COMPAT_SUNOS - vec.sv_flags &= ~SA_NOCLDSTOP; -#endif error = copyout(&vec, uap->osv, sizeof(vec)); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct osigblock_args { int mask; }; #endif /* * MPSAFE */ int osigblock(td, uap) register struct thread *td; struct osigblock_args *uap; { struct proc *p = td->td_proc; sigset_t set; OSIG2SIG(uap->mask, set); SIG_CANTMASK(set); PROC_LOCK(p); SIG2OSIG(td->td_sigmask, td->td_retval[0]); SIGSETOR(td->td_sigmask, set); PROC_UNLOCK(p); return (0); } #ifndef _SYS_SYSPROTO_H_ struct osigsetmask_args { int mask; }; #endif /* * MPSAFE */ int osigsetmask(td, uap) struct thread *td; struct osigsetmask_args *uap; { struct proc *p = td->td_proc; sigset_t set; OSIG2SIG(uap->mask, set); SIG_CANTMASK(set); PROC_LOCK(p); SIG2OSIG(td->td_sigmask, td->td_retval[0]); SIGSETLO(td->td_sigmask, set); signotify(td); PROC_UNLOCK(p); return (0); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ /* * Suspend process until signal, providing mask to be set * in the meantime. ***** XXXKSE this doesn't make sense under KSE. ***** Do we suspend the thread or all threads in the process? ***** How do we suspend threads running NOW on another processor? */ #ifndef _SYS_SYSPROTO_H_ struct sigsuspend_args { const sigset_t *sigmask; }; #endif /* * MPSAFE */ /* ARGSUSED */ int sigsuspend(td, uap) struct thread *td; struct sigsuspend_args *uap; { sigset_t mask; int error; error = copyin(uap->sigmask, &mask, sizeof(mask)); if (error) return (error); return (kern_sigsuspend(td, mask)); } int kern_sigsuspend(struct thread *td, sigset_t mask) { struct proc *p = td->td_proc; /* * When returning from sigsuspend, we want * the old mask to be restored after the * signal handler has finished. Thus, we * save it here and mark the sigacts structure * to indicate this. */ PROC_LOCK(p); td->td_oldsigmask = td->td_sigmask; td->td_pflags |= TDP_OLDMASK; SIG_CANTMASK(mask); td->td_sigmask = mask; signotify(td); while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "pause", 0) == 0) /* void */; PROC_UNLOCK(p); /* always return EINTR rather than ERESTART... */ return (EINTR); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ /* * Compatibility sigsuspend call for old binaries. Note nonstandard calling * convention: libc stub passes mask, not pointer, to save a copyin. */ #ifndef _SYS_SYSPROTO_H_ struct osigsuspend_args { osigset_t mask; }; #endif /* * MPSAFE */ /* ARGSUSED */ int osigsuspend(td, uap) struct thread *td; struct osigsuspend_args *uap; { struct proc *p = td->td_proc; sigset_t mask; PROC_LOCK(p); td->td_oldsigmask = td->td_sigmask; td->td_pflags |= TDP_OLDMASK; OSIG2SIG(uap->mask, mask); SIG_CANTMASK(mask); SIGSETLO(td->td_sigmask, mask); signotify(td); while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "opause", 0) == 0) /* void */; PROC_UNLOCK(p); /* always return EINTR rather than ERESTART... */ return (EINTR); } #endif /* COMPAT_43 */ -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct osigstack_args { struct sigstack *nss; struct sigstack *oss; }; #endif /* * MPSAFE */ /* ARGSUSED */ int osigstack(td, uap) struct thread *td; register struct osigstack_args *uap; { struct sigstack nss, oss; int error = 0; if (uap->nss != NULL) { error = copyin(uap->nss, &nss, sizeof(nss)); if (error) return (error); } oss.ss_sp = td->td_sigstk.ss_sp; oss.ss_onstack = sigonstack(cpu_getstack(td)); if (uap->nss != NULL) { td->td_sigstk.ss_sp = nss.ss_sp; td->td_sigstk.ss_size = 0; td->td_sigstk.ss_flags |= nss.ss_onstack & SS_ONSTACK; td->td_pflags |= TDP_ALTSTACK; } if (uap->oss != NULL) error = copyout(&oss, uap->oss, sizeof(oss)); return (error); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ #ifndef _SYS_SYSPROTO_H_ struct sigaltstack_args { stack_t *ss; stack_t *oss; }; #endif /* * MPSAFE */ /* ARGSUSED */ int sigaltstack(td, uap) struct thread *td; register struct sigaltstack_args *uap; { stack_t ss, oss; int error; if (uap->ss != NULL) { error = copyin(uap->ss, &ss, sizeof(ss)); if (error) return (error); } error = kern_sigaltstack(td, (uap->ss != NULL) ? &ss : NULL, (uap->oss != NULL) ? &oss : NULL); if (error) return (error); if (uap->oss != NULL) error = copyout(&oss, uap->oss, sizeof(stack_t)); return (error); } int kern_sigaltstack(struct thread *td, stack_t *ss, stack_t *oss) { struct proc *p = td->td_proc; int oonstack; oonstack = sigonstack(cpu_getstack(td)); if (oss != NULL) { *oss = td->td_sigstk; oss->ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; } if (ss != NULL) { if (oonstack) return (EPERM); if ((ss->ss_flags & ~SS_DISABLE) != 0) return (EINVAL); if (!(ss->ss_flags & SS_DISABLE)) { if (ss->ss_size < p->p_sysent->sv_minsigstksz) { return (ENOMEM); } td->td_sigstk = *ss; td->td_pflags |= TDP_ALTSTACK; } else { td->td_pflags &= ~TDP_ALTSTACK; } } return (0); } /* * Common code for kill process group/broadcast kill. * cp is calling process. */ static int killpg1(td, sig, pgid, all) register struct thread *td; int sig, pgid, all; { register struct proc *p; struct pgrp *pgrp; int nfound = 0; if (all) { /* * broadcast */ sx_slock(&allproc_lock); LIST_FOREACH(p, &allproc, p_list) { PROC_LOCK(p); if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || p == td->td_proc) { PROC_UNLOCK(p); continue; } if (p_cansignal(td, p, sig) == 0) { nfound++; if (sig) psignal(p, sig); } PROC_UNLOCK(p); } sx_sunlock(&allproc_lock); } else { sx_slock(&proctree_lock); if (pgid == 0) { /* * zero pgid means send to my process group. */ pgrp = td->td_proc->p_pgrp; PGRP_LOCK(pgrp); } else { pgrp = pgfind(pgid); if (pgrp == NULL) { sx_sunlock(&proctree_lock); return (ESRCH); } } sx_sunlock(&proctree_lock); LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (p->p_pid <= 1 || p->p_flag & P_SYSTEM) { PROC_UNLOCK(p); continue; } if (p->p_state == PRS_ZOMBIE) { PROC_UNLOCK(p); continue; } if (p_cansignal(td, p, sig) == 0) { nfound++; if (sig) psignal(p, sig); } PROC_UNLOCK(p); } PGRP_UNLOCK(pgrp); } return (nfound ? 0 : ESRCH); } #ifndef _SYS_SYSPROTO_H_ struct kill_args { int pid; int signum; }; #endif /* * MPSAFE */ /* ARGSUSED */ int kill(td, uap) register struct thread *td; register struct kill_args *uap; { register struct proc *p; int error; if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); if (uap->pid > 0) { /* kill single process */ if ((p = pfind(uap->pid)) == NULL) return (ESRCH); error = p_cansignal(td, p, uap->signum); if (error == 0 && uap->signum) psignal(p, uap->signum); PROC_UNLOCK(p); return (error); } switch (uap->pid) { case -1: /* broadcast signal */ return (killpg1(td, uap->signum, 0, 1)); case 0: /* signal own process group */ return (killpg1(td, uap->signum, 0, 0)); default: /* negative explicit process group */ return (killpg1(td, uap->signum, -uap->pid, 0)); } /* NOTREACHED */ } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct okillpg_args { int pgid; int signum; }; #endif /* * MPSAFE */ /* ARGSUSED */ int okillpg(td, uap) struct thread *td; register struct okillpg_args *uap; { if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); return (killpg1(td, uap->signum, uap->pgid, 0)); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ /* * Send a signal to a process group. */ void gsignal(pgid, sig) int pgid, sig; { struct pgrp *pgrp; if (pgid != 0) { sx_slock(&proctree_lock); pgrp = pgfind(pgid); sx_sunlock(&proctree_lock); if (pgrp != NULL) { pgsignal(pgrp, sig, 0); PGRP_UNLOCK(pgrp); } } } /* * Send a signal to a process group. If checktty is 1, * limit to members which have a controlling terminal. */ void pgsignal(pgrp, sig, checkctty) struct pgrp *pgrp; int sig, checkctty; { register struct proc *p; if (pgrp) { PGRP_LOCK_ASSERT(pgrp, MA_OWNED); LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (checkctty == 0 || p->p_flag & P_CONTROLT) psignal(p, sig); PROC_UNLOCK(p); } } } /* * Send a signal caused by a trap to the current thread. * If it will be caught immediately, deliver it with correct code. * Otherwise, post it normally. * * MPSAFE */ void trapsignal(struct thread *td, int sig, u_long code) { struct sigacts *ps; struct proc *p; siginfo_t siginfo; int error; p = td->td_proc; if (td->td_pflags & TDP_SA) { if (td->td_mailbox == NULL) thread_user_enter(p, td); PROC_LOCK(p); if (td->td_mailbox) { SIGDELSET(td->td_sigmask, sig); mtx_lock_spin(&sched_lock); /* * Force scheduling an upcall, so UTS has chance to * process the signal before thread runs again in * userland. */ if (td->td_upcall) td->td_upcall->ku_flags |= KUF_DOUPCALL; mtx_unlock_spin(&sched_lock); } else { /* UTS caused a sync signal */ p->p_code = code; /* XXX for core dump/debugger */ p->p_sig = sig; /* XXX to verify code */ sigexit(td, sig); } } else { PROC_LOCK(p); } ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); if ((p->p_flag & P_TRACED) == 0 && SIGISMEMBER(ps->ps_sigcatch, sig) && !SIGISMEMBER(td->td_sigmask, sig)) { p->p_stats->p_ru.ru_nsignals++; #ifdef KTRACE if (KTRPOINT(curthread, KTR_PSIG)) ktrpsig(sig, ps->ps_sigact[_SIG_IDX(sig)], &td->td_sigmask, code); #endif if (!(td->td_pflags & TDP_SA)) (*p->p_sysent->sv_sendsig)( ps->ps_sigact[_SIG_IDX(sig)], sig, &td->td_sigmask, code); else { cpu_thread_siginfo(sig, code, &siginfo); mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); error = copyout(&siginfo, &td->td_mailbox->tm_syncsig, sizeof(siginfo)); PROC_LOCK(p); /* UTS memory corrupted */ if (error) sigexit(td, SIGILL); SIGADDSET(td->td_sigmask, sig); mtx_lock(&ps->ps_mtx); } SIGSETOR(td->td_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]); if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(td->td_sigmask, sig); if (SIGISMEMBER(ps->ps_sigreset, sig)) { /* * See kern_sigaction() for origin of this code. */ SIGDELSET(ps->ps_sigcatch, sig); if (sig != SIGCONT && sigprop(sig) & SA_IGNORE) SIGADDSET(ps->ps_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } mtx_unlock(&ps->ps_mtx); } else { mtx_unlock(&ps->ps_mtx); p->p_code = code; /* XXX for core dump/debugger */ p->p_sig = sig; /* XXX to verify code */ tdsignal(td, sig, SIGTARGET_TD); } PROC_UNLOCK(p); } static struct thread * sigtd(struct proc *p, int sig, int prop) { struct thread *td, *signal_td; PROC_LOCK_ASSERT(p, MA_OWNED); /* * First find a thread in sigwait state and signal belongs to * its wait set. POSIX's arguments is that speed of delivering signal * to sigwait thread is faster than delivering signal to user stack. * If we can not find sigwait thread, then find the first thread in * the proc that doesn't have this signal masked, an exception is * if current thread is sending signal to its process, and it does not * mask the signal, it should get the signal, this is another fast * way to deliver signal. */ signal_td = NULL; mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td) { if (td->td_waitset != NULL && SIGISMEMBER(*(td->td_waitset), sig)) { mtx_unlock_spin(&sched_lock); return (td); } if (!SIGISMEMBER(td->td_sigmask, sig)) { if (td == curthread) signal_td = curthread; else if (signal_td == NULL) signal_td = td; } } if (signal_td == NULL) signal_td = FIRST_THREAD_IN_PROC(p); mtx_unlock_spin(&sched_lock); return (signal_td); } /* * Send the signal to the process. If the signal has an action, the action * is usually performed by the target process rather than the caller; we add * the signal to the set of pending signals for the process. * * Exceptions: * o When a stop signal is sent to a sleeping process that takes the * default action, the process is stopped without awakening it. * o SIGCONT restarts stopped processes (or puts them back to sleep) * regardless of the signal action (eg, blocked or ignored). * * Other ignored signals are discarded immediately. * * MPSAFE */ void psignal(struct proc *p, int sig) { struct thread *td; int prop; if (!_SIG_VALID(sig)) panic("psignal(): invalid signal"); PROC_LOCK_ASSERT(p, MA_OWNED); prop = sigprop(sig); /* * Find a thread to deliver the signal to. */ td = sigtd(p, sig, prop); tdsignal(td, sig, SIGTARGET_P); } /* * MPSAFE */ void tdsignal(struct thread *td, int sig, sigtarget_t target) { sigset_t saved; struct proc *p = td->td_proc; if (p->p_flag & P_SA) saved = p->p_siglist; do_tdsignal(td, sig, target); if ((p->p_flag & P_SA) && !(p->p_flag & P_SIGEVENT)) { if (SIGSETEQ(saved, p->p_siglist)) return; else { /* pending set changed */ p->p_flag |= P_SIGEVENT; wakeup(&p->p_siglist); } } } static void do_tdsignal(struct thread *td, int sig, sigtarget_t target) { struct proc *p; register sig_t action; sigset_t *siglist; struct thread *td0; register int prop; struct sigacts *ps; if (!_SIG_VALID(sig)) panic("do_tdsignal(): invalid signal"); p = td->td_proc; ps = p->p_sigacts; PROC_LOCK_ASSERT(p, MA_OWNED); KNOTE(&p->p_klist, NOTE_SIGNAL | sig); prop = sigprop(sig); /* * If the signal is blocked and not destined for this thread, then * assign it to the process so that we can find it later in the first * thread that unblocks it. Otherwise, assign it to this thread now. */ if (target == SIGTARGET_TD) { siglist = &td->td_siglist; } else { if (!SIGISMEMBER(td->td_sigmask, sig)) siglist = &td->td_siglist; else if (td->td_waitset != NULL && SIGISMEMBER(*(td->td_waitset), sig)) siglist = &td->td_siglist; else siglist = &p->p_siglist; } /* * If proc is traced, always give parent a chance; * if signal event is tracked by procfs, give *that* * a chance, as well. */ if ((p->p_flag & P_TRACED) || (p->p_stops & S_SIG)) { action = SIG_DFL; } else { /* * If the signal is being ignored, * then we forget about it immediately. * (Note: we don't set SIGCONT in ps_sigignore, * and if it is set to SIG_IGN, * action will be SIG_DFL here.) */ mtx_lock(&ps->ps_mtx); if (SIGISMEMBER(ps->ps_sigignore, sig) || (p->p_flag & P_WEXIT)) { mtx_unlock(&ps->ps_mtx); return; } if (((td->td_waitset == NULL) && SIGISMEMBER(td->td_sigmask, sig)) || ((td->td_waitset != NULL) && SIGISMEMBER(td->td_sigmask, sig) && !SIGISMEMBER(*(td->td_waitset), sig))) action = SIG_HOLD; else if (SIGISMEMBER(ps->ps_sigcatch, sig)) action = SIG_CATCH; else action = SIG_DFL; mtx_unlock(&ps->ps_mtx); } if (prop & SA_CONT) { SIG_STOPSIGMASK(p->p_siglist); /* * XXX Should investigate leaving STOP and CONT sigs only in * the proc's siglist. */ mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) SIG_STOPSIGMASK(td0->td_siglist); mtx_unlock_spin(&sched_lock); } if (prop & SA_STOP) { /* * If sending a tty stop signal to a member of an orphaned * process group, discard the signal here if the action * is default; don't stop the process below if sleeping, * and don't clear any pending SIGCONT. */ if ((prop & SA_TTYSTOP) && (p->p_pgrp->pg_jobc == 0) && (action == SIG_DFL)) return; SIG_CONTSIGMASK(p->p_siglist); mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) SIG_CONTSIGMASK(td0->td_siglist); mtx_unlock_spin(&sched_lock); p->p_flag &= ~P_CONTINUED; } SIGADDSET(*siglist, sig); signotify(td); /* uses schedlock */ if (siglist == &td->td_siglist && (td->td_waitset != NULL) && action != SIG_HOLD) { td->td_waitset = NULL; } /* * Defer further processing for signals which are held, * except that stopped processes must be continued by SIGCONT. */ if (action == SIG_HOLD && !((prop & SA_CONT) && (p->p_flag & P_STOPPED_SIG))) return; /* * Some signals have a process-wide effect and a per-thread * component. Most processing occurs when the process next * tries to cross the user boundary, however there are some * times when processing needs to be done immediatly, such as * waking up threads so that they can cross the user boundary. * We try do the per-process part here. */ if (P_SHOULDSTOP(p)) { /* * The process is in stopped mode. All the threads should be * either winding down or already on the suspended queue. */ if (p->p_flag & P_TRACED) { /* * The traced process is already stopped, * so no further action is necessary. * No signal can restart us. */ goto out; } if (sig == SIGKILL) { /* * SIGKILL sets process running. * It will die elsewhere. * All threads must be restarted. */ p->p_flag &= ~P_STOPPED; goto runfast; } if (prop & SA_CONT) { /* * If SIGCONT is default (or ignored), we continue the * process but don't leave the signal in siglist as * it has no further action. If SIGCONT is held, we * continue the process and leave the signal in * siglist. If the process catches SIGCONT, let it * handle the signal itself. If it isn't waiting on * an event, it goes back to run state. * Otherwise, process goes back to sleep state. */ p->p_flag &= ~P_STOPPED_SIG; p->p_flag |= P_CONTINUED; if (action == SIG_DFL) { SIGDELSET(*siglist, sig); } else if (action == SIG_CATCH) { /* * The process wants to catch it so it needs * to run at least one thread, but which one? * It would seem that the answer would be to * run an upcall in the next KSE to run, and * deliver the signal that way. In a NON KSE * process, we need to make sure that the * single thread is runnable asap. * XXXKSE for now however, make them all run. */ goto runfast; } /* * The signal is not ignored or caught. */ mtx_lock_spin(&sched_lock); thread_unsuspend(p); mtx_unlock_spin(&sched_lock); goto out; } if (prop & SA_STOP) { /* * Already stopped, don't need to stop again * (If we did the shell could get confused). * Just make sure the signal STOP bit set. */ p->p_flag |= P_STOPPED_SIG; SIGDELSET(*siglist, sig); goto out; } /* * All other kinds of signals: * If a thread is sleeping interruptibly, simulate a * wakeup so that when it is continued it will be made * runnable and can look at the signal. However, don't make * the PROCESS runnable, leave it stopped. * It may run a bit until it hits a thread_suspend_check(). */ mtx_lock_spin(&sched_lock); if (TD_ON_SLEEPQ(td) && (td->td_flags & TDF_SINTR)) sleepq_abort(td); mtx_unlock_spin(&sched_lock); goto out; /* * Mutexes are short lived. Threads waiting on them will * hit thread_suspend_check() soon. */ } else if (p->p_state == PRS_NORMAL) { if ((p->p_flag & P_TRACED) || (action != SIG_DFL) || !(prop & SA_STOP)) { mtx_lock_spin(&sched_lock); tdsigwakeup(td, sig, action); mtx_unlock_spin(&sched_lock); goto out; } if (prop & SA_STOP) { if (p->p_flag & P_PPWAIT) goto out; p->p_flag |= P_STOPPED_SIG; p->p_xstat = sig; mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) { if (TD_IS_SLEEPING(td0) && (td0->td_flags & TDF_SINTR) && !TD_IS_SUSPENDED(td0)) { thread_suspend_one(td0); } else if (td != td0) { td0->td_flags |= TDF_ASTPENDING; } } thread_stopped(p); if (p->p_numthreads == p->p_suspcount) { SIGDELSET(p->p_siglist, p->p_xstat); FOREACH_THREAD_IN_PROC(p, td0) SIGDELSET(td0->td_siglist, p->p_xstat); } mtx_unlock_spin(&sched_lock); goto out; } else goto runfast; /* NOTREACHED */ } else { /* Not in "NORMAL" state. discard the signal. */ SIGDELSET(*siglist, sig); goto out; } /* * The process is not stopped so we need to apply the signal to all the * running threads. */ runfast: mtx_lock_spin(&sched_lock); tdsigwakeup(td, sig, action); thread_unsuspend(p); mtx_unlock_spin(&sched_lock); out: /* If we jump here, sched_lock should not be owned. */ mtx_assert(&sched_lock, MA_NOTOWNED); } /* * The force of a signal has been directed against a single * thread. We need to see what we can do about knocking it * out of any sleep it may be in etc. */ static void tdsigwakeup(struct thread *td, int sig, sig_t action) { struct proc *p = td->td_proc; register int prop; PROC_LOCK_ASSERT(p, MA_OWNED); mtx_assert(&sched_lock, MA_OWNED); prop = sigprop(sig); /* * Bring the priority of a thread up if we want it to get * killed in this lifetime. */ if (action == SIG_DFL && (prop & SA_KILL)) { if (td->td_priority > PUSER) td->td_priority = PUSER; } if (TD_ON_SLEEPQ(td)) { /* * If thread is sleeping uninterruptibly * we can't interrupt the sleep... the signal will * be noticed when the process returns through * trap() or syscall(). */ if ((td->td_flags & TDF_SINTR) == 0) return; /* * Process is sleeping and traced. Make it runnable * so it can discover the signal in issignal() and stop * for its parent. */ if (p->p_flag & P_TRACED) { p->p_flag &= ~P_STOPPED_TRACE; } else { /* * If SIGCONT is default (or ignored) and process is * asleep, we are finished; the process should not * be awakened. */ if ((prop & SA_CONT) && action == SIG_DFL) { SIGDELSET(p->p_siglist, sig); /* * It may be on either list in this state. * Remove from both for now. */ SIGDELSET(td->td_siglist, sig); return; } /* * Give low priority threads a better chance to run. */ if (td->td_priority > PUSER) td->td_priority = PUSER; } sleepq_abort(td); } else { /* * Other states do nothing with the signal immediately, * other than kicking ourselves if we are running. * It will either never be noticed, or noticed very soon. */ #ifdef SMP if (TD_IS_RUNNING(td) && td != curthread) forward_signal(td); #endif } } void ptracestop(struct thread *td, int sig) { struct proc *p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &p->p_mtx.mtx_object, "Stopping for traced signal"); p->p_xstat = sig; PROC_LOCK(p->p_pptr); psignal(p->p_pptr, SIGCHLD); PROC_UNLOCK(p->p_pptr); stop(p); mtx_lock_spin(&sched_lock); thread_suspend_one(td); PROC_UNLOCK(p); DROP_GIANT(); mi_switch(SW_INVOL); mtx_unlock_spin(&sched_lock); PICKUP_GIANT(); } /* * If the current process has received a signal (should be caught or cause * termination, should interrupt current syscall), return the signal number. * Stop signals with default action are processed immediately, then cleared; * they aren't returned. This is checked after each entry to the system for * a syscall or trap (though this can usually be done without calling issignal * by checking the pending signal masks in cursig.) The normal call * sequence is * * while (sig = cursig(curthread)) * postsig(sig); */ static int issignal(td) struct thread *td; { struct proc *p; struct sigacts *ps; sigset_t sigpending; int sig, prop; struct thread *td0; p = td->td_proc; ps = p->p_sigacts; mtx_assert(&ps->ps_mtx, MA_OWNED); PROC_LOCK_ASSERT(p, MA_OWNED); for (;;) { int traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG); sigpending = td->td_siglist; SIGSETNAND(sigpending, td->td_sigmask); if (p->p_flag & P_PPWAIT) SIG_STOPSIGMASK(sigpending); if (SIGISEMPTY(sigpending)) /* no signal to send */ return (0); sig = sig_ffs(&sigpending); if (p->p_stops & S_SIG) { mtx_unlock(&ps->ps_mtx); stopevent(p, S_SIG, sig); mtx_lock(&ps->ps_mtx); } /* * We should see pending but ignored signals * only if P_TRACED was on when they were posted. */ if (SIGISMEMBER(ps->ps_sigignore, sig) && (traced == 0)) { SIGDELSET(td->td_siglist, sig); continue; } if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { /* * If traced, always stop. */ mtx_unlock(&ps->ps_mtx); ptracestop(td, sig); PROC_LOCK(p); mtx_lock(&ps->ps_mtx); /* * If parent wants us to take the signal, * then it will leave it in p->p_xstat; * otherwise we just look for signals again. */ SIGDELSET(td->td_siglist, sig); /* clear old signal */ sig = p->p_xstat; if (sig == 0) continue; /* * If the traced bit got turned off, go back up * to the top to rescan signals. This ensures * that p_sig* and p_sigact are consistent. */ if ((p->p_flag & P_TRACED) == 0) continue; /* * Put the new signal into td_siglist. If the * signal is being masked, look for other signals. */ SIGADDSET(td->td_siglist, sig); if (SIGISMEMBER(td->td_sigmask, sig)) continue; signotify(td); } prop = sigprop(sig); /* * Decide whether the signal should be returned. * Return the signal's number, or fall through * to clear it from the pending mask. */ switch ((intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) { case (intptr_t)SIG_DFL: /* * Don't take default actions on system processes. */ if (p->p_pid <= 1) { #ifdef DIAGNOSTIC /* * Are you sure you want to ignore SIGSEGV * in init? XXX */ printf("Process (pid %lu) got signal %d\n", (u_long)p->p_pid, sig); #endif break; /* == ignore */ } /* * If there is a pending stop signal to process * with default action, stop here, * then clear the signal. However, * if process is member of an orphaned * process group, ignore tty stop signals. */ if (prop & SA_STOP) { if (p->p_flag & P_TRACED || (p->p_pgrp->pg_jobc == 0 && prop & SA_TTYSTOP)) break; /* == ignore */ mtx_unlock(&ps->ps_mtx); WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &p->p_mtx.mtx_object, "Catching SIGSTOP"); p->p_flag |= P_STOPPED_SIG; p->p_xstat = sig; mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) { if (TD_IS_SLEEPING(td0) && (td0->td_flags & TDF_SINTR) && !TD_IS_SUSPENDED(td0)) { thread_suspend_one(td0); } else if (td != td0) { td0->td_flags |= TDF_ASTPENDING; } } thread_stopped(p); thread_suspend_one(td); PROC_UNLOCK(p); DROP_GIANT(); mi_switch(SW_INVOL); mtx_unlock_spin(&sched_lock); PICKUP_GIANT(); PROC_LOCK(p); mtx_lock(&ps->ps_mtx); break; } else if (prop & SA_IGNORE) { /* * Except for SIGCONT, shouldn't get here. * Default action is to ignore; drop it. */ break; /* == ignore */ } else return (sig); /*NOTREACHED*/ case (intptr_t)SIG_IGN: /* * Masking above should prevent us ever trying * to take action on an ignored signal other * than SIGCONT, unless process is traced. */ if ((prop & SA_CONT) == 0 && (p->p_flag & P_TRACED) == 0) printf("issignal\n"); break; /* == ignore */ default: /* * This signal has an action, let * postsig() process it. */ return (sig); } SIGDELSET(td->td_siglist, sig); /* take the signal! */ } /* NOTREACHED */ } /* * Put the argument process into the stopped state and notify the parent * via wakeup. Signals are handled elsewhere. The process must not be * on the run queue. Must be called with the proc p locked. */ static void stop(struct proc *p) { PROC_LOCK_ASSERT(p, MA_OWNED); p->p_flag |= P_STOPPED_SIG; p->p_flag &= ~P_WAITED; wakeup(p->p_pptr); } /* * MPSAFE */ void thread_stopped(struct proc *p) { struct proc *p1 = curthread->td_proc; struct sigacts *ps; int n; PROC_LOCK_ASSERT(p, MA_OWNED); mtx_assert(&sched_lock, MA_OWNED); n = p->p_suspcount; if (p == p1) n++; if ((p->p_flag & P_STOPPED_SIG) && (n == p->p_numthreads)) { mtx_unlock_spin(&sched_lock); stop(p); PROC_LOCK(p->p_pptr); ps = p->p_pptr->p_sigacts; mtx_lock(&ps->ps_mtx); if ((ps->ps_flag & PS_NOCLDSTOP) == 0) { mtx_unlock(&ps->ps_mtx); psignal(p->p_pptr, SIGCHLD); } else mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p->p_pptr); mtx_lock_spin(&sched_lock); } } /* * Take the action for the specified signal * from the current set of pending signals. */ void postsig(sig) register int sig; { struct thread *td = curthread; register struct proc *p = td->td_proc; struct sigacts *ps; sig_t action; sigset_t returnmask; int code; KASSERT(sig != 0, ("postsig")); PROC_LOCK_ASSERT(p, MA_OWNED); ps = p->p_sigacts; mtx_assert(&ps->ps_mtx, MA_OWNED); SIGDELSET(td->td_siglist, sig); action = ps->ps_sigact[_SIG_IDX(sig)]; #ifdef KTRACE if (KTRPOINT(td, KTR_PSIG)) ktrpsig(sig, action, td->td_pflags & TDP_OLDMASK ? &td->td_oldsigmask : &td->td_sigmask, 0); #endif if (p->p_stops & S_SIG) { mtx_unlock(&ps->ps_mtx); stopevent(p, S_SIG, sig); mtx_lock(&ps->ps_mtx); } if (!(td->td_pflags & TDP_SA && td->td_mailbox) && action == SIG_DFL) { /* * Default action, where the default is to kill * the process. (Other cases were ignored above.) */ mtx_unlock(&ps->ps_mtx); sigexit(td, sig); /* NOTREACHED */ } else { if (td->td_pflags & TDP_SA && td->td_mailbox) { if (sig == SIGKILL) { mtx_unlock(&ps->ps_mtx); sigexit(td, sig); } } /* * If we get here, the signal must be caught. */ KASSERT(action != SIG_IGN && !SIGISMEMBER(td->td_sigmask, sig), ("postsig action")); /* * Set the new mask value and also defer further * occurrences of this signal. * * Special case: user has done a sigsuspend. Here the * current mask is not of interest, but rather the * mask from before the sigsuspend is what we want * restored after the signal processing is completed. */ if (td->td_pflags & TDP_OLDMASK) { returnmask = td->td_oldsigmask; td->td_pflags &= ~TDP_OLDMASK; } else returnmask = td->td_sigmask; SIGSETOR(td->td_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]); if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(td->td_sigmask, sig); if (SIGISMEMBER(ps->ps_sigreset, sig)) { /* * See kern_sigaction() for origin of this code. */ SIGDELSET(ps->ps_sigcatch, sig); if (sig != SIGCONT && sigprop(sig) & SA_IGNORE) SIGADDSET(ps->ps_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } p->p_stats->p_ru.ru_nsignals++; if (p->p_sig != sig) { code = 0; } else { code = p->p_code; p->p_code = 0; p->p_sig = 0; } if (td->td_pflags & TDP_SA && td->td_mailbox) thread_signal_add(curthread, sig); else (*p->p_sysent->sv_sendsig)(action, sig, &returnmask, code); } } /* * Kill the current process for stated reason. */ void killproc(p, why) struct proc *p; char *why; { PROC_LOCK_ASSERT(p, MA_OWNED); CTR3(KTR_PROC, "killproc: proc %p (pid %d, %s)", p, p->p_pid, p->p_comm); log(LOG_ERR, "pid %d (%s), uid %d, was killed: %s\n", p->p_pid, p->p_comm, p->p_ucred ? p->p_ucred->cr_uid : -1, why); psignal(p, SIGKILL); } /* * Force the current process to exit with the specified signal, dumping core * if appropriate. We bypass the normal tests for masked and caught signals, * allowing unrecoverable failures to terminate the process without changing * signal state. Mark the accounting record with the signal termination. * If dumping core, save the signal number for the debugger. Calls exit and * does not return. * * MPSAFE */ void sigexit(td, sig) struct thread *td; int sig; { struct proc *p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); p->p_acflag |= AXSIG; if (sigprop(sig) & SA_CORE) { p->p_sig = sig; /* * Log signals which would cause core dumps * (Log as LOG_INFO to appease those who don't want * these messages.) * XXX : Todo, as well as euid, write out ruid too * Note that coredump() drops proc lock. */ if (coredump(td) == 0) sig |= WCOREFLAG; if (kern_logsigexit) log(LOG_INFO, "pid %d (%s), uid %d: exited on signal %d%s\n", p->p_pid, p->p_comm, td->td_ucred ? td->td_ucred->cr_uid : -1, sig &~ WCOREFLAG, sig & WCOREFLAG ? " (core dumped)" : ""); } else PROC_UNLOCK(p); exit1(td, W_EXITCODE(0, sig)); /* NOTREACHED */ } static char corefilename[MAXPATHLEN+1] = {"%N.core"}; SYSCTL_STRING(_kern, OID_AUTO, corefile, CTLFLAG_RW, corefilename, sizeof(corefilename), "process corefile name format string"); /* * expand_name(name, uid, pid) * Expand the name described in corefilename, using name, uid, and pid. * corefilename is a printf-like string, with three format specifiers: * %N name of process ("name") * %P process id (pid) * %U user id (uid) * For example, "%N.core" is the default; they can be disabled completely * by using "/dev/null", or all core files can be stored in "/cores/%U/%N-%P". * This is controlled by the sysctl variable kern.corefile (see above). */ static char * expand_name(name, uid, pid) const char *name; uid_t uid; pid_t pid; { const char *format, *appendstr; char *temp; char buf[11]; /* Buffer for pid/uid -- max 4B */ size_t i, l, n; format = corefilename; temp = malloc(MAXPATHLEN, M_TEMP, M_NOWAIT | M_ZERO); if (temp == NULL) return (NULL); for (i = 0, n = 0; n < MAXPATHLEN && format[i]; i++) { switch (format[i]) { case '%': /* Format character */ i++; switch (format[i]) { case '%': appendstr = "%"; break; case 'N': /* process name */ appendstr = name; break; case 'P': /* process id */ sprintf(buf, "%u", pid); appendstr = buf; break; case 'U': /* user id */ sprintf(buf, "%u", uid); appendstr = buf; break; default: appendstr = ""; log(LOG_ERR, "Unknown format character %c in `%s'\n", format[i], format); } l = strlen(appendstr); if ((n + l) >= MAXPATHLEN) goto toolong; memcpy(temp + n, appendstr, l); n += l; break; default: temp[n++] = format[i]; } } if (format[i] != '\0') goto toolong; return (temp); toolong: log(LOG_ERR, "pid %ld (%s), uid (%lu): corename is too long\n", (long)pid, name, (u_long)uid); free(temp, M_TEMP); return (NULL); } /* * Dump a process' core. The main routine does some * policy checking, and creates the name of the coredump; * then it passes on a vnode and a size limit to the process-specific * coredump routine if there is one; if there _is not_ one, it returns * ENOSYS; otherwise it returns the error from the process-specific routine. */ static int coredump(struct thread *td) { struct proc *p = td->td_proc; register struct vnode *vp; register struct ucred *cred = td->td_ucred; struct flock lf; struct nameidata nd; struct vattr vattr; int error, error1, flags, locked; struct mount *mp; char *name; /* name of corefile */ off_t limit; PROC_LOCK_ASSERT(p, MA_OWNED); _STOPEVENT(p, S_CORE, 0); if (((sugid_coredump == 0) && p->p_flag & P_SUGID) || do_coredump == 0) { PROC_UNLOCK(p); return (EFAULT); } /* * Note that the bulk of limit checking is done after * the corefile is created. The exception is if the limit * for corefiles is 0, in which case we don't bother * creating the corefile at all. This layout means that * a corefile is truncated instead of not being created, * if it is larger than the limit. */ limit = (off_t)lim_cur(p, RLIMIT_CORE); PROC_UNLOCK(p); if (limit == 0) return (EFBIG); mtx_lock(&Giant); restart: name = expand_name(p->p_comm, td->td_ucred->cr_uid, p->p_pid); if (name == NULL) { mtx_unlock(&Giant); return (EINVAL); } NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, td); /* XXXKSE */ flags = O_CREAT | FWRITE | O_NOFOLLOW; error = vn_open(&nd, &flags, S_IRUSR | S_IWUSR, -1); free(name, M_TEMP); if (error) { mtx_unlock(&Giant); return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; /* Don't dump to non-regular files or files with links. */ if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred, td) || vattr.va_nlink != 1) { VOP_UNLOCK(vp, 0, td); error = EFAULT; goto out; } VOP_UNLOCK(vp, 0, td); lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_WRLCK; locked = (VOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &lf, F_FLOCK) == 0); if (vn_start_write(vp, &mp, V_NOWAIT) != 0) { lf.l_type = F_UNLCK; if (locked) VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &lf, F_FLOCK); if ((error = vn_close(vp, FWRITE, cred, td)) != 0) return (error); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_size = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VOP_LEASE(vp, td, cred, LEASE_WRITE); VOP_SETATTR(vp, &vattr, cred, td); VOP_UNLOCK(vp, 0, td); PROC_LOCK(p); p->p_acflag |= ACORE; PROC_UNLOCK(p); error = p->p_sysent->sv_coredump ? p->p_sysent->sv_coredump(td, vp, limit) : ENOSYS; if (locked) { lf.l_type = F_UNLCK; VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &lf, F_FLOCK); } vn_finished_write(mp); out: error1 = vn_close(vp, FWRITE, cred, td); mtx_unlock(&Giant); if (error == 0) error = error1; return (error); } /* * Nonexistent system call-- signal process (may want to handle it). * Flag error in case process won't see signal immediately (blocked or ignored). */ #ifndef _SYS_SYSPROTO_H_ struct nosys_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int nosys(td, args) struct thread *td; struct nosys_args *args; { struct proc *p = td->td_proc; PROC_LOCK(p); psignal(p, SIGSYS); PROC_UNLOCK(p); return (ENOSYS); } /* * Send a SIGIO or SIGURG signal to a process or process group using * stored credentials rather than those of the current process. */ void pgsigio(sigiop, sig, checkctty) struct sigio **sigiop; int sig, checkctty; { struct sigio *sigio; SIGIO_LOCK(); sigio = *sigiop; if (sigio == NULL) { SIGIO_UNLOCK(); return; } if (sigio->sio_pgid > 0) { PROC_LOCK(sigio->sio_proc); if (CANSIGIO(sigio->sio_ucred, sigio->sio_proc->p_ucred)) psignal(sigio->sio_proc, sig); PROC_UNLOCK(sigio->sio_proc); } else if (sigio->sio_pgid < 0) { struct proc *p; PGRP_LOCK(sigio->sio_pgrp); LIST_FOREACH(p, &sigio->sio_pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (CANSIGIO(sigio->sio_ucred, p->p_ucred) && (checkctty == 0 || (p->p_flag & P_CONTROLT))) psignal(p, sig); PROC_UNLOCK(p); } PGRP_UNLOCK(sigio->sio_pgrp); } SIGIO_UNLOCK(); } static int filt_sigattach(struct knote *kn) { struct proc *p = curproc; kn->kn_ptr.p_proc = p; kn->kn_flags |= EV_CLEAR; /* automatically set */ PROC_LOCK(p); SLIST_INSERT_HEAD(&p->p_klist, kn, kn_selnext); PROC_UNLOCK(p); return (0); } static void filt_sigdetach(struct knote *kn) { struct proc *p = kn->kn_ptr.p_proc; PROC_LOCK(p); SLIST_REMOVE(&p->p_klist, kn, knote, kn_selnext); PROC_UNLOCK(p); } /* * signal knotes are shared with proc knotes, so we apply a mask to * the hint in order to differentiate them from process hints. This * could be avoided by using a signal-specific knote list, but probably * isn't worth the trouble. */ static int filt_signal(struct knote *kn, long hint) { if (hint & NOTE_SIGNAL) { hint &= ~NOTE_SIGNAL; if (kn->kn_id == hint) kn->kn_data++; } return (kn->kn_data != 0); } struct sigacts * sigacts_alloc(void) { struct sigacts *ps; ps = malloc(sizeof(struct sigacts), M_SUBPROC, M_WAITOK | M_ZERO); ps->ps_refcnt = 1; mtx_init(&ps->ps_mtx, "sigacts", NULL, MTX_DEF); return (ps); } void sigacts_free(struct sigacts *ps) { mtx_lock(&ps->ps_mtx); ps->ps_refcnt--; if (ps->ps_refcnt == 0) { mtx_destroy(&ps->ps_mtx); free(ps, M_SUBPROC); } else mtx_unlock(&ps->ps_mtx); } struct sigacts * sigacts_hold(struct sigacts *ps) { mtx_lock(&ps->ps_mtx); ps->ps_refcnt++; mtx_unlock(&ps->ps_mtx); return (ps); } void sigacts_copy(struct sigacts *dest, struct sigacts *src) { KASSERT(dest->ps_refcnt == 1, ("sigacts_copy to shared dest")); mtx_lock(&src->ps_mtx); bcopy(src, dest, offsetof(struct sigacts, ps_refcnt)); mtx_unlock(&src->ps_mtx); } int sigacts_shared(struct sigacts *ps) { int shared; mtx_lock(&ps->ps_mtx); shared = ps->ps_refcnt > 1; mtx_unlock(&ps->ps_mtx); return (shared); } Index: head/sys/kern/kern_xxx.c =================================================================== --- head/sys/kern/kern_xxx.c (revision 130343) +++ head/sys/kern/kern_xxx.c (revision 130344) @@ -1,313 +1,313 @@ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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. * 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. * * @(#)kern_xxx.c 8.2 (Berkeley) 11/14/93 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #include #include -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct gethostname_args { char *hostname; u_int len; }; #endif /* * MPSAFE */ /* ARGSUSED */ int ogethostname(td, uap) struct thread *td; struct gethostname_args *uap; { int name[2]; int error; size_t len = uap->len; name[0] = CTL_KERN; name[1] = KERN_HOSTNAME; mtx_lock(&Giant); error = userland_sysctl(td, name, 2, uap->hostname, &len, 1, 0, 0, 0); mtx_unlock(&Giant); return(error); } #ifndef _SYS_SYSPROTO_H_ struct sethostname_args { char *hostname; u_int len; }; #endif /* * MPSAFE */ /* ARGSUSED */ int osethostname(td, uap) struct thread *td; register struct sethostname_args *uap; { int name[2]; int error; name[0] = CTL_KERN; name[1] = KERN_HOSTNAME; mtx_lock(&Giant); if ((error = suser_cred(td->td_ucred, PRISON_ROOT)) == 0) { error = userland_sysctl(td, name, 2, 0, 0, 0, uap->hostname, uap->len, 0); } mtx_unlock(&Giant); return (error); } #ifndef _SYS_SYSPROTO_H_ struct ogethostid_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int ogethostid(td, uap) struct thread *td; struct ogethostid_args *uap; { *(long *)(td->td_retval) = hostid; return (0); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ #ifdef COMPAT_43 #ifndef _SYS_SYSPROTO_H_ struct osethostid_args { long hostid; }; #endif /* * MPSAFE */ /* ARGSUSED */ int osethostid(td, uap) struct thread *td; struct osethostid_args *uap; { int error; if ((error = suser(td))) return (error); mtx_lock(&Giant); hostid = uap->hostid; mtx_unlock(&Giant); return (0); } /* * MPSAFE */ int oquota(td, uap) struct thread *td; struct oquota_args *uap; { return (ENOSYS); } #endif /* COMPAT_43 */ /* * This is the FreeBSD-1.1 compatable uname(2) interface. These * days it is done in libc as a wrapper around a bunch of sysctl's. * This must maintain the old 1.1 binary ABI. */ #if SYS_NMLN != 32 #error "FreeBSD-1.1 uname syscall has been broken" #endif #ifndef _SYS_SYSPROTO_H_ struct uname_args { struct utsname *name; }; #endif /* * MPSAFE */ /* ARGSUSED */ int uname(td, uap) struct thread *td; struct uname_args *uap; { int name[2], error; size_t len; char *s, *us; name[0] = CTL_KERN; name[1] = KERN_OSTYPE; len = sizeof (uap->name->sysname); mtx_lock(&Giant); error = userland_sysctl(td, name, 2, uap->name->sysname, &len, 1, 0, 0, 0); if (error) goto done2; subyte( uap->name->sysname + sizeof(uap->name->sysname) - 1, 0); name[1] = KERN_HOSTNAME; len = sizeof uap->name->nodename; error = userland_sysctl(td, name, 2, uap->name->nodename, &len, 1, 0, 0, 0); if (error) goto done2; subyte( uap->name->nodename + sizeof(uap->name->nodename) - 1, 0); name[1] = KERN_OSRELEASE; len = sizeof uap->name->release; error = userland_sysctl(td, name, 2, uap->name->release, &len, 1, 0, 0, 0); if (error) goto done2; subyte( uap->name->release + sizeof(uap->name->release) - 1, 0); /* name = KERN_VERSION; len = sizeof uap->name->version; error = userland_sysctl(td, name, 2, uap->name->version, &len, 1, 0, 0, 0); if (error) goto done2; subyte( uap->name->version + sizeof(uap->name->version) - 1, 0); */ /* * this stupid hackery to make the version field look like FreeBSD 1.1 */ for(s = version; *s && *s != '#'; s++); for(us = uap->name->version; *s && *s != ':'; s++) { error = subyte( us++, *s); if (error) goto done2; } error = subyte( us++, 0); if (error) goto done2; name[0] = CTL_HW; name[1] = HW_MACHINE; len = sizeof uap->name->machine; error = userland_sysctl(td, name, 2, uap->name->machine, &len, 1, 0, 0, 0); if (error) goto done2; subyte( uap->name->machine + sizeof(uap->name->machine) - 1, 0); done2: mtx_unlock(&Giant); return (error); } #ifndef _SYS_SYSPROTO_H_ struct getdomainname_args { char *domainname; int len; }; #endif /* * MPSAFE */ /* ARGSUSED */ int getdomainname(td, uap) struct thread *td; struct getdomainname_args *uap; { int domainnamelen; int error; mtx_lock(&Giant); domainnamelen = strlen(domainname) + 1; if ((u_int)uap->len > domainnamelen) uap->len = domainnamelen; error = copyout(domainname, uap->domainname, uap->len); mtx_unlock(&Giant); return (error); } #ifndef _SYS_SYSPROTO_H_ struct setdomainname_args { char *domainname; int len; }; #endif /* * MPSAFE */ /* ARGSUSED */ int setdomainname(td, uap) struct thread *td; struct setdomainname_args *uap; { int error, domainnamelen; mtx_lock(&Giant); if ((error = suser(td))) goto done2; if ((u_int)uap->len > sizeof (domainname) - 1) { error = EINVAL; goto done2; } domainnamelen = uap->len; error = copyin(uap->domainname, domainname, uap->len); domainname[domainnamelen] = 0; done2: mtx_unlock(&Giant); return (error); } Index: head/sys/kern/tty.c =================================================================== --- head/sys/kern/tty.c (revision 130343) +++ head/sys/kern/tty.c (revision 130344) @@ -1,2852 +1,2852 @@ /*- * Copyright (c) 1982, 1986, 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Copyright (c) 2002 Networks Associates Technologies, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * 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. * 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. * * @(#)tty.c 8.8 (Berkeley) 1/21/94 */ /*- * TODO: * o Fix races for sending the start char in ttyflush(). * o Handle inter-byte timeout for "MIN > 0, TIME > 0" in ttyselect(). * With luck, there will be MIN chars before select() returns(). * o Handle CLOCAL consistently for ptys. Perhaps disallow setting it. * o Don't allow input in TS_ZOMBIE case. It would be visible through * FIONREAD. * o Do the new sio locking stuff here and use it to avoid special * case for EXTPROC? * o Lock PENDIN too? * o Move EXTPROC and/or PENDIN to t_state? * o Wrap most of ttioctl in spltty/splx. * o Implement TIOCNOTTY or remove it from . * o Send STOP if IXOFF is toggled off while TS_TBLOCK is set. * o Don't allow certain termios flags to affect disciplines other * than TTYDISC. Cancel their effects before switch disciplines * and ignore them if they are set while we are in another * discipline. * o Now that historical speed conversions are handled here, don't * do them in drivers. * o Check for TS_CARR_ON being set while everything is closed and not * waiting for carrier. TS_CARR_ON isn't cleared if nothing is open, * so it would live until the next open even if carrier drops. * o Restore TS_WOPEN since it is useful in pstat. It must be cleared * only when _all_ openers leave open(). */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_tty.h" #include #include #include #include #include #include #include -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #include #endif #include #define TTYDEFCHARS #include #undef TTYDEFCHARS #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_TTYS, "ttys", "tty data structures"); long tk_cancc; long tk_nin; long tk_nout; long tk_rawcc; static int proc_compare(struct proc *p1, struct proc *p2); static int ttnread(struct tty *tp); static void ttyecho(int c, struct tty *tp); static int ttyoutput(int c, struct tty *tp); static void ttypend(struct tty *tp); static void ttyretype(struct tty *tp); static void ttyrub(int c, struct tty *tp); static void ttyrubo(struct tty *tp, int cnt); static void ttyunblock(struct tty *tp); static int ttywflush(struct tty *tp); static int filt_ttyread(struct knote *kn, long hint); static void filt_ttyrdetach(struct knote *kn); static int filt_ttywrite(struct knote *kn, long hint); static void filt_ttywdetach(struct knote *kn); /* * Table with character classes and parity. The 8th bit indicates parity, * the 7th bit indicates the character is an alphameric or underscore (for * ALTWERASE), and the low 6 bits indicate delay type. If the low 6 bits * are 0 then the character needs no special processing on output; classes * other than 0 might be translated or (not currently) require delays. */ #define E 0x00 /* Even parity. */ #define O 0x80 /* Odd parity. */ #define PARITY(c) (char_type[c] & O) #define ALPHA 0x40 /* Alpha or underscore. */ #define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA) #define CCLASSMASK 0x3f #define CCLASS(c) (char_type[c] & CCLASSMASK) #define BS BACKSPACE #define CC CONTROL #define CR RETURN #define NA ORDINARY | ALPHA #define NL NEWLINE #define NO ORDINARY #define TB TAB #define VT VTAB static u_char const char_type[] = { E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */ O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */ O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */ E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */ O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */ E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */ O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */ O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */ O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */ E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */ O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */ O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */ E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */ /* * Meta chars; should be settable per character set; * for now, treat them all as normal characters. */ NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, }; #undef BS #undef CC #undef CR #undef NA #undef NL #undef NO #undef TB #undef VT /* Macros to clear/set/test flags. */ #define SET(t, f) (t) |= (f) #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) #undef MAX_INPUT /* XXX wrong in */ #define MAX_INPUT TTYHOG /* XXX limit is usually larger for !ICANON */ /* * list of struct tty where pstat(8) can pick it up with sysctl * * The lock order is to grab the list mutex before the tty mutex. * Together with additions going on the tail of the list, this allows * the sysctl to avoid doing retries. */ static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list); static struct mtx tty_list_mutex; static int drainwait = 5*60; SYSCTL_INT(_kern, OID_AUTO, drainwait, CTLFLAG_RW, &drainwait, 0, "Output drain timeout in seconds"); /* * Initial open of tty, or (re)entry to standard tty line discipline. */ int ttyopen(dev_t device, struct tty *tp) { int s; s = spltty(); tp->t_dev = device; if (!ISSET(tp->t_state, TS_ISOPEN)) { ttyref(tp); SET(tp->t_state, TS_ISOPEN); if (ISSET(tp->t_cflag, CLOCAL)) SET(tp->t_state, TS_CONNECTED); bzero(&tp->t_winsize, sizeof(tp->t_winsize)); } /* XXX don't hang forever on output */ if (tp->t_timeout < 0) tp->t_timeout = drainwait*hz; ttsetwater(tp); splx(s); return (0); } /* * Handle close() on a tty line: flush and set to initial state, * bumping generation number so that pending read/write calls * can detect recycling of the tty. * XXX our caller should have done `spltty(); l_close(); ttyclose();' * and l_close() should have flushed, but we repeat the spltty() and * the flush in case there are buggy callers. */ int ttyclose(struct tty *tp) { int s; funsetown(&tp->t_sigio); s = spltty(); if (constty == tp) constty_clear(); ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_canq); clist_free_cblocks(&tp->t_outq); clist_free_cblocks(&tp->t_rawq); tp->t_gen++; tp->t_line = TTYDISC; tp->t_pgrp = NULL; tp->t_session = NULL; tp->t_state = 0; ttyrel(tp); splx(s); return (0); } #define FLUSHQ(q) { \ if ((q)->c_cc) \ ndflush(q, (q)->c_cc); \ } /* Is 'c' a line delimiter ("break" character)? */ #define TTBREAKC(c, lflag) \ ((c) == '\n' || (((c) == cc[VEOF] || \ (c) == cc[VEOL] || ((c) == cc[VEOL2] && lflag & IEXTEN)) && \ (c) != _POSIX_VDISABLE)) /* * Process input of a single character received on a tty. */ int ttyinput(int c, struct tty *tp) { tcflag_t iflag, lflag; cc_t *cc; int i, err; /* * If input is pending take it first. */ lflag = tp->t_lflag; if (ISSET(lflag, PENDIN)) ttypend(tp); /* * Gather stats. */ if (ISSET(lflag, ICANON)) { ++tk_cancc; ++tp->t_cancc; } else { ++tk_rawcc; ++tp->t_rawcc; } ++tk_nin; /* * Block further input iff: * current input > threshold AND input is available to user program * AND input flow control is enabled and not yet invoked. * The 3 is slop for PARMRK. */ iflag = tp->t_iflag; if (tp->t_rawq.c_cc + tp->t_canq.c_cc > tp->t_ihiwat - 3 && (!ISSET(lflag, ICANON) || tp->t_canq.c_cc != 0) && (ISSET(tp->t_cflag, CRTS_IFLOW) || ISSET(iflag, IXOFF)) && !ISSET(tp->t_state, TS_TBLOCK)) ttyblock(tp); /* Handle exceptional conditions (break, parity, framing). */ cc = tp->t_cc; err = (ISSET(c, TTY_ERRORMASK)); if (err) { CLR(c, TTY_ERRORMASK); if (ISSET(err, TTY_BI)) { if (ISSET(iflag, IGNBRK)) return (0); if (ISSET(iflag, BRKINT)) { ttyflush(tp, FREAD | FWRITE); if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGINT, 1); PGRP_UNLOCK(tp->t_pgrp); } goto endcase; } if (ISSET(iflag, PARMRK)) goto parmrk; } else if ((ISSET(err, TTY_PE) && ISSET(iflag, INPCK)) || ISSET(err, TTY_FE)) { if (ISSET(iflag, IGNPAR)) return (0); else if (ISSET(iflag, PARMRK)) { parmrk: if (tp->t_rawq.c_cc + tp->t_canq.c_cc > MAX_INPUT - 3) goto input_overflow; (void)putc(0377 | TTY_QUOTE, &tp->t_rawq); (void)putc(0 | TTY_QUOTE, &tp->t_rawq); (void)putc(c | TTY_QUOTE, &tp->t_rawq); goto endcase; } else c = 0; } } if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP)) CLR(c, 0x80); if (!ISSET(lflag, EXTPROC)) { /* * Check for literal nexting very first */ if (ISSET(tp->t_state, TS_LNCH)) { SET(c, TTY_QUOTE); CLR(tp->t_state, TS_LNCH); } /* * Scan for special characters. This code * is really just a big case statement with * non-constant cases. The bottom of the * case statement is labeled ``endcase'', so goto * it after a case match, or similar. */ /* * Control chars which aren't controlled * by ICANON, ISIG, or IXON. */ if (ISSET(lflag, IEXTEN)) { if (CCEQ(cc[VLNEXT], c)) { if (ISSET(lflag, ECHO)) { if (ISSET(lflag, ECHOE)) { (void)ttyoutput('^', tp); (void)ttyoutput('\b', tp); } else ttyecho(c, tp); } SET(tp->t_state, TS_LNCH); goto endcase; } if (CCEQ(cc[VDISCARD], c)) { if (ISSET(lflag, FLUSHO)) CLR(tp->t_lflag, FLUSHO); else { ttyflush(tp, FWRITE); ttyecho(c, tp); if (tp->t_rawq.c_cc + tp->t_canq.c_cc) ttyretype(tp); SET(tp->t_lflag, FLUSHO); } goto startoutput; } } /* * Signals. */ if (ISSET(lflag, ISIG)) { if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) { if (!ISSET(lflag, NOFLSH)) ttyflush(tp, FREAD | FWRITE); ttyecho(c, tp); if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1); PGRP_UNLOCK(tp->t_pgrp); } goto endcase; } if (CCEQ(cc[VSUSP], c)) { if (!ISSET(lflag, NOFLSH)) ttyflush(tp, FREAD); ttyecho(c, tp); if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGTSTP, 1); PGRP_UNLOCK(tp->t_pgrp); } goto endcase; } } /* * Handle start/stop characters. */ if (ISSET(iflag, IXON)) { if (CCEQ(cc[VSTOP], c)) { if (!ISSET(tp->t_state, TS_TTSTOP)) { SET(tp->t_state, TS_TTSTOP); (*tp->t_stop)(tp, 0); return (0); } if (!CCEQ(cc[VSTART], c)) return (0); /* * if VSTART == VSTOP then toggle */ goto endcase; } if (CCEQ(cc[VSTART], c)) goto restartoutput; } /* * IGNCR, ICRNL, & INLCR */ if (c == '\r') { if (ISSET(iflag, IGNCR)) return (0); else if (ISSET(iflag, ICRNL)) c = '\n'; } else if (c == '\n' && ISSET(iflag, INLCR)) c = '\r'; } if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) { /* * From here on down canonical mode character * processing takes place. */ /* * erase or erase2 (^H / ^?) */ if (CCEQ(cc[VERASE], c) || CCEQ(cc[VERASE2], c) ) { if (tp->t_rawq.c_cc) ttyrub(unputc(&tp->t_rawq), tp); goto endcase; } /* * kill (^U) */ if (CCEQ(cc[VKILL], c)) { if (ISSET(lflag, ECHOKE) && tp->t_rawq.c_cc == tp->t_rocount && !ISSET(lflag, ECHOPRT)) while (tp->t_rawq.c_cc) ttyrub(unputc(&tp->t_rawq), tp); else { ttyecho(c, tp); if (ISSET(lflag, ECHOK) || ISSET(lflag, ECHOKE)) ttyecho('\n', tp); FLUSHQ(&tp->t_rawq); tp->t_rocount = 0; } CLR(tp->t_state, TS_LOCAL); goto endcase; } /* * word erase (^W) */ if (CCEQ(cc[VWERASE], c) && ISSET(lflag, IEXTEN)) { int ctype; /* * erase whitespace */ while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t') ttyrub(c, tp); if (c == -1) goto endcase; /* * erase last char of word and remember the * next chars type (for ALTWERASE) */ ttyrub(c, tp); c = unputc(&tp->t_rawq); if (c == -1) goto endcase; if (c == ' ' || c == '\t') { (void)putc(c, &tp->t_rawq); goto endcase; } ctype = ISALPHA(c); /* * erase rest of word */ do { ttyrub(c, tp); c = unputc(&tp->t_rawq); if (c == -1) goto endcase; } while (c != ' ' && c != '\t' && (!ISSET(lflag, ALTWERASE) || ISALPHA(c) == ctype)); (void)putc(c, &tp->t_rawq); goto endcase; } /* * reprint line (^R) */ if (CCEQ(cc[VREPRINT], c) && ISSET(lflag, IEXTEN)) { ttyretype(tp); goto endcase; } /* * ^T - kernel info and generate SIGINFO */ if (CCEQ(cc[VSTATUS], c) && ISSET(lflag, IEXTEN)) { if (ISSET(lflag, ISIG) && tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGINFO, 1); PGRP_UNLOCK(tp->t_pgrp); } if (!ISSET(lflag, NOKERNINFO)) ttyinfo(tp); goto endcase; } } /* * Check for input buffer overflow */ if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= MAX_INPUT) { input_overflow: if (ISSET(iflag, IMAXBEL)) { if (tp->t_outq.c_cc < tp->t_ohiwat) (void)ttyoutput(CTRL('g'), tp); } goto endcase; } if ( c == 0377 && ISSET(iflag, PARMRK) && !ISSET(iflag, ISTRIP) && ISSET(iflag, IGNBRK|IGNPAR) != (IGNBRK|IGNPAR)) (void)putc(0377 | TTY_QUOTE, &tp->t_rawq); /* * Put data char in q for user and * wakeup on seeing a line delimiter. */ if (putc(c, &tp->t_rawq) >= 0) { if (!ISSET(lflag, ICANON)) { ttwakeup(tp); ttyecho(c, tp); goto endcase; } if (TTBREAKC(c, lflag)) { tp->t_rocount = 0; catq(&tp->t_rawq, &tp->t_canq); ttwakeup(tp); } else if (tp->t_rocount++ == 0) tp->t_rocol = tp->t_column; if (ISSET(tp->t_state, TS_ERASE)) { /* * end of prterase \.../ */ CLR(tp->t_state, TS_ERASE); (void)ttyoutput('/', tp); } i = tp->t_column; ttyecho(c, tp); if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) { /* * Place the cursor over the '^' of the ^D. */ i = imin(2, tp->t_column - i); while (i > 0) { (void)ttyoutput('\b', tp); i--; } } } endcase: /* * IXANY means allow any character to restart output. */ if (ISSET(tp->t_state, TS_TTSTOP) && !ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP]) return (0); restartoutput: CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_TTSTOP); startoutput: return (ttstart(tp)); } /* * Output a single character on a tty, doing output processing * as needed (expanding tabs, newline processing, etc.). * Returns < 0 if succeeds, otherwise returns char to resend. * Must be recursive. */ static int ttyoutput(int c, struct tty *tp) { tcflag_t oflag; int col, s; oflag = tp->t_oflag; if (!ISSET(oflag, OPOST)) { if (ISSET(tp->t_lflag, FLUSHO)) return (-1); if (putc(c, &tp->t_outq)) return (c); tk_nout++; tp->t_outcc++; return (-1); } /* * Do tab expansion if OXTABS is set. Special case if we external * processing, we don't do the tab expansion because we'll probably * get it wrong. If tab expansion needs to be done, let it happen * externally. */ CLR(c, ~TTY_CHARMASK); if (c == '\t' && ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) { c = 8 - (tp->t_column & 7); if (!ISSET(tp->t_lflag, FLUSHO)) { s = spltty(); /* Don't interrupt tabs. */ c -= b_to_q(" ", c, &tp->t_outq); tk_nout += c; tp->t_outcc += c; splx(s); } tp->t_column += c; return (c ? -1 : '\t'); } if (c == CEOT && ISSET(oflag, ONOEOT)) return (-1); /* * Newline translation: if ONLCR is set, * translate newline into "\r\n". */ if (c == '\n' && ISSET(tp->t_oflag, ONLCR)) { tk_nout++; tp->t_outcc++; if (!ISSET(tp->t_lflag, FLUSHO) && putc('\r', &tp->t_outq)) return (c); } /* If OCRNL is set, translate "\r" into "\n". */ else if (c == '\r' && ISSET(tp->t_oflag, OCRNL)) c = '\n'; /* If ONOCR is set, don't transmit CRs when on column 0. */ else if (c == '\r' && ISSET(tp->t_oflag, ONOCR) && tp->t_column == 0) return (-1); tk_nout++; tp->t_outcc++; if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq)) return (c); col = tp->t_column; switch (CCLASS(c)) { case BACKSPACE: if (col > 0) --col; break; case CONTROL: break; case NEWLINE: if (ISSET(tp->t_oflag, ONLCR | ONLRET)) col = 0; break; case RETURN: col = 0; break; case ORDINARY: ++col; break; case TAB: col = (col + 8) & ~7; break; } tp->t_column = col; return (-1); } /* * Ioctls for all tty devices. Called after line-discipline specific ioctl * has been called to do discipline-specific functions and/or reject any * of these ioctl commands. */ /* ARGSUSED */ int ttioctl(struct tty *tp, u_long cmd, void *data, int flag) { struct proc *p; struct thread *td; struct pgrp *pgrp; int s, error; td = curthread; /* XXX */ p = td->td_proc; /* If the ioctl involves modification, hang if in the background. */ switch (cmd) { case TIOCCBRK: case TIOCCONS: case TIOCDRAIN: case TIOCEXCL: case TIOCFLUSH: #ifdef TIOCHPCL case TIOCHPCL: #endif case TIOCNXCL: case TIOCSBRK: case TIOCSCTTY: case TIOCSDRAINWAIT: case TIOCSETA: case TIOCSETAF: case TIOCSETAW: case TIOCSETD: case TIOCSPGRP: case TIOCSTART: case TIOCSTAT: case TIOCSTI: case TIOCSTOP: case TIOCSWINSZ: -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) case TIOCLBIC: case TIOCLBIS: case TIOCLSET: case TIOCSETC: case OTIOCSETD: case TIOCSETN: case TIOCSETP: case TIOCSLTC: #endif sx_slock(&proctree_lock); PROC_LOCK(p); while (isbackground(p, tp) && !(p->p_flag & P_PPWAIT) && !SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTOU) && !SIGISMEMBER(td->td_sigmask, SIGTTOU)) { pgrp = p->p_pgrp; PROC_UNLOCK(p); if (pgrp->pg_jobc == 0) { sx_sunlock(&proctree_lock); return (EIO); } PGRP_LOCK(pgrp); sx_sunlock(&proctree_lock); pgsignal(pgrp, SIGTTOU, 1); PGRP_UNLOCK(pgrp); error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, "ttybg1", 0); if (error) return (error); sx_slock(&proctree_lock); PROC_LOCK(p); } PROC_UNLOCK(p); sx_sunlock(&proctree_lock); break; } switch (cmd) { /* Process the ioctl. */ case FIOASYNC: /* set/clear async i/o */ s = spltty(); if (*(int *)data) SET(tp->t_state, TS_ASYNC); else CLR(tp->t_state, TS_ASYNC); splx(s); break; case FIONBIO: /* set/clear non-blocking i/o */ break; /* XXX: delete. */ case FIONREAD: /* get # bytes to read */ s = spltty(); *(int *)data = ttnread(tp); splx(s); break; case FIOSETOWN: /* * Policy -- Don't allow FIOSETOWN on someone else's * controlling tty */ if (tp->t_session != NULL && !isctty(p, tp)) return (ENOTTY); error = fsetown(*(int *)data, &tp->t_sigio); if (error) return (error); break; case FIOGETOWN: if (tp->t_session != NULL && !isctty(p, tp)) return (ENOTTY); *(int *)data = fgetown(&tp->t_sigio); break; case TIOCEXCL: /* set exclusive use of tty */ s = spltty(); SET(tp->t_state, TS_XCLUDE); splx(s); break; case TIOCFLUSH: { /* flush buffers */ int flags = *(int *)data; if (flags == 0) flags = FREAD | FWRITE; else flags &= FREAD | FWRITE; ttyflush(tp, flags); break; } case TIOCCONS: /* become virtual console */ if (*(int *)data) { struct nameidata nid; if (constty && constty != tp && ISSET(constty->t_state, TS_CONNECTED)) return (EBUSY); /* Ensure user can open the real console. */ NDINIT(&nid, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, "/dev/console", td); if ((error = namei(&nid)) != 0) return (error); NDFREE(&nid, NDF_ONLY_PNBUF); error = VOP_ACCESS(nid.ni_vp, VREAD, td->td_ucred, td); vput(nid.ni_vp); if (error) return (error); constty_set(tp); } else if (tp == constty) constty_clear(); break; case TIOCDRAIN: /* wait till output drained */ error = ttywait(tp); if (error) return (error); break; case TIOCGETA: { /* get termios struct */ struct termios *t = (struct termios *)data; bcopy(&tp->t_termios, t, sizeof(struct termios)); break; } case TIOCGETD: /* get line discipline */ *(int *)data = tp->t_line; break; case TIOCGWINSZ: /* get window size */ *(struct winsize *)data = tp->t_winsize; break; case TIOCGPGRP: /* get pgrp of tty */ if (!isctty(p, tp)) return (ENOTTY); *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; break; #ifdef TIOCHPCL case TIOCHPCL: /* hang up on last close */ s = spltty(); SET(tp->t_cflag, HUPCL); splx(s); break; #endif case TIOCNXCL: /* reset exclusive use of tty */ s = spltty(); CLR(tp->t_state, TS_XCLUDE); splx(s); break; case TIOCOUTQ: /* output queue size */ *(int *)data = tp->t_outq.c_cc; break; case TIOCSETA: /* set termios struct */ case TIOCSETAW: /* drain output, set */ case TIOCSETAF: { /* drn out, fls in, set */ struct termios *t = (struct termios *)data; if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (t->c_ispeed == 0) t->c_ispeed = tp->t_ospeed; if (t->c_ispeed == 0) return (EINVAL); s = spltty(); if (cmd == TIOCSETAW || cmd == TIOCSETAF) { error = ttywait(tp); if (error) { splx(s); return (error); } if (cmd == TIOCSETAF) ttyflush(tp, FREAD); } if (!ISSET(t->c_cflag, CIGNORE)) { /* * Set device hardware. */ if (tp->t_param && (error = (*tp->t_param)(tp, t))) { splx(s); return (error); } if (ISSET(t->c_cflag, CLOCAL) && !ISSET(tp->t_cflag, CLOCAL)) { /* * XXX disconnections would be too hard to * get rid of without this kludge. The only * way to get rid of controlling terminals * is to exit from the session leader. */ CLR(tp->t_state, TS_ZOMBIE); wakeup(TSA_CARR_ON(tp)); ttwakeup(tp); ttwwakeup(tp); } if ((ISSET(tp->t_state, TS_CARR_ON) || ISSET(t->c_cflag, CLOCAL)) && !ISSET(tp->t_state, TS_ZOMBIE)) SET(tp->t_state, TS_CONNECTED); else CLR(tp->t_state, TS_CONNECTED); tp->t_cflag = t->c_cflag; tp->t_ispeed = t->c_ispeed; if (t->c_ospeed != 0) tp->t_ospeed = t->c_ospeed; ttsetwater(tp); } if (ISSET(t->c_lflag, ICANON) != ISSET(tp->t_lflag, ICANON) && cmd != TIOCSETAF) { if (ISSET(t->c_lflag, ICANON)) SET(tp->t_lflag, PENDIN); else { /* * XXX we really shouldn't allow toggling * ICANON while we're in a non-termios line * discipline. Now we have to worry about * panicing for a null queue. */ if (tp->t_canq.c_cbreserved > 0 && tp->t_rawq.c_cbreserved > 0) { catq(&tp->t_rawq, &tp->t_canq); /* * XXX the queue limits may be * different, so the old queue * swapping method no longer works. */ catq(&tp->t_canq, &tp->t_rawq); } CLR(tp->t_lflag, PENDIN); } ttwakeup(tp); } tp->t_iflag = t->c_iflag; tp->t_oflag = t->c_oflag; /* * Make the EXTPROC bit read only. */ if (ISSET(tp->t_lflag, EXTPROC)) SET(t->c_lflag, EXTPROC); else CLR(t->c_lflag, EXTPROC); tp->t_lflag = t->c_lflag | ISSET(tp->t_lflag, PENDIN); if (t->c_cc[VMIN] != tp->t_cc[VMIN] || t->c_cc[VTIME] != tp->t_cc[VTIME]) ttwakeup(tp); bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc)); splx(s); break; } case TIOCSETD: { /* set line discipline */ int t = *(int *)data; dev_t device = tp->t_dev; if ((u_int)t >= nlinesw) return (ENXIO); if (t != tp->t_line) { s = spltty(); ttyld_close(tp, flag); error = (*linesw[t]->l_open)(device, tp); if (error) { (void)ttyld_open(tp, device); splx(s); return (error); } tp->t_line = t; splx(s); } break; } case TIOCSTART: /* start output, like ^Q */ s = spltty(); if (ISSET(tp->t_state, TS_TTSTOP) || ISSET(tp->t_lflag, FLUSHO)) { CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_TTSTOP); ttstart(tp); } splx(s); break; case TIOCSTI: /* simulate terminal input */ if ((flag & FREAD) == 0 && suser(td)) return (EPERM); if (!isctty(p, tp) && suser(td)) return (EACCES); s = spltty(); ttyld_rint(tp, *(u_char *)data); splx(s); break; case TIOCSTOP: /* stop output, like ^S */ s = spltty(); if (!ISSET(tp->t_state, TS_TTSTOP)) { SET(tp->t_state, TS_TTSTOP); (*tp->t_stop)(tp, 0); } splx(s); break; case TIOCSCTTY: /* become controlling tty */ /* Session ctty vnode pointer set in vnode layer. */ sx_slock(&proctree_lock); if (!SESS_LEADER(p) || ((p->p_session->s_ttyvp || tp->t_session) && (tp->t_session != p->p_session))) { sx_sunlock(&proctree_lock); return (EPERM); } tp->t_session = p->p_session; tp->t_pgrp = p->p_pgrp; SESS_LOCK(p->p_session); ttyref(tp); /* ttyrel(): kern_proc.c:pgdelete() */ p->p_session->s_ttyp = tp; SESS_UNLOCK(p->p_session); PROC_LOCK(p); p->p_flag |= P_CONTROLT; PROC_UNLOCK(p); sx_sunlock(&proctree_lock); break; case TIOCSPGRP: { /* set pgrp of tty */ sx_slock(&proctree_lock); pgrp = pgfind(*(int *)data); if (!isctty(p, tp)) { if (pgrp != NULL) PGRP_UNLOCK(pgrp); sx_sunlock(&proctree_lock); return (ENOTTY); } if (pgrp == NULL) { sx_sunlock(&proctree_lock); return (EPERM); } PGRP_UNLOCK(pgrp); if (pgrp->pg_session != p->p_session) { sx_sunlock(&proctree_lock); return (EPERM); } sx_sunlock(&proctree_lock); tp->t_pgrp = pgrp; break; } case TIOCSTAT: /* simulate control-T */ s = spltty(); ttyinfo(tp); splx(s); break; case TIOCSWINSZ: /* set window size */ if (bcmp((caddr_t)&tp->t_winsize, data, sizeof (struct winsize))) { tp->t_winsize = *(struct winsize *)data; if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGWINCH, 1); PGRP_UNLOCK(tp->t_pgrp); } } break; case TIOCSDRAINWAIT: error = suser(td); if (error) return (error); tp->t_timeout = *(int *)data * hz; wakeup(TSA_OCOMPLETE(tp)); wakeup(TSA_OLOWAT(tp)); break; case TIOCGDRAINWAIT: *(int *)data = tp->t_timeout / hz; break; default: -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) return (ttcompat(tp, cmd, data, flag)); #else return (ENOIOCTL); #endif } return (0); } int ttypoll(dev_t dev, int events, struct thread *td) { int s; int revents = 0; struct tty *tp; KASSERT(devsw(dev)->d_flags & D_TTY, ("ttypoll() called on non D_TTY device (%s)", devtoname(dev))); tp = dev->si_tty; KASSERT(tp != NULL, ("ttypoll(): no tty pointer on device (%s)", devtoname(dev))); if (tp == NULL) /* XXX used to return ENXIO, but that means true! */ return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP); s = spltty(); if (events & (POLLIN | POLLRDNORM)) { if (ttnread(tp) > 0 || ISSET(tp->t_state, TS_ZOMBIE)) revents |= events & (POLLIN | POLLRDNORM); else selrecord(td, &tp->t_rsel); } if (events & (POLLOUT | POLLWRNORM)) { if ((tp->t_outq.c_cc <= tp->t_olowat && ISSET(tp->t_state, TS_CONNECTED)) || ISSET(tp->t_state, TS_ZOMBIE)) revents |= events & (POLLOUT | POLLWRNORM); else selrecord(td, &tp->t_wsel); } splx(s); return (revents); } static struct filterops ttyread_filtops = { 1, NULL, filt_ttyrdetach, filt_ttyread }; static struct filterops ttywrite_filtops = { 1, NULL, filt_ttywdetach, filt_ttywrite }; int ttykqfilter(dev_t dev, struct knote *kn) { struct tty *tp; struct klist *klist; int s; KASSERT(devsw(dev)->d_flags & D_TTY, ("ttykqfilter() called on non D_TTY device (%s)", devtoname(dev))); tp = dev->si_tty; KASSERT(tp != NULL, ("ttykqfilter(): no tty pointer on device (%s)", devtoname(dev))); switch (kn->kn_filter) { case EVFILT_READ: klist = &tp->t_rsel.si_note; kn->kn_fop = &ttyread_filtops; break; case EVFILT_WRITE: klist = &tp->t_wsel.si_note; kn->kn_fop = &ttywrite_filtops; break; default: return (1); } kn->kn_hook = (caddr_t)dev; s = spltty(); SLIST_INSERT_HEAD(klist, kn, kn_selnext); splx(s); return (0); } static void filt_ttyrdetach(struct knote *kn) { struct tty *tp = ((dev_t)kn->kn_hook)->si_tty; int s = spltty(); SLIST_REMOVE(&tp->t_rsel.si_note, kn, knote, kn_selnext); splx(s); } static int filt_ttyread(struct knote *kn, long hint) { struct tty *tp = ((dev_t)kn->kn_hook)->si_tty; kn->kn_data = ttnread(tp); if (ISSET(tp->t_state, TS_ZOMBIE)) { kn->kn_flags |= EV_EOF; return (1); } return (kn->kn_data > 0); } static void filt_ttywdetach(struct knote *kn) { struct tty *tp = ((dev_t)kn->kn_hook)->si_tty; int s = spltty(); SLIST_REMOVE(&tp->t_wsel.si_note, kn, knote, kn_selnext); splx(s); } static int filt_ttywrite(struct knote *kn, long hint) { struct tty *tp = ((dev_t)kn->kn_hook)->si_tty; kn->kn_data = tp->t_outq.c_cc; if (ISSET(tp->t_state, TS_ZOMBIE)) return (1); return (kn->kn_data <= tp->t_olowat && ISSET(tp->t_state, TS_CONNECTED)); } /* * Must be called at spltty(). */ static int ttnread(struct tty *tp) { int nread; if (ISSET(tp->t_lflag, PENDIN)) ttypend(tp); nread = tp->t_canq.c_cc; if (!ISSET(tp->t_lflag, ICANON)) { nread += tp->t_rawq.c_cc; if (nread < tp->t_cc[VMIN] && tp->t_cc[VTIME] == 0) nread = 0; } return (nread); } /* * Wait for output to drain. */ int ttywait(struct tty *tp) { int error, s; error = 0; s = spltty(); while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && ISSET(tp->t_state, TS_CONNECTED) && tp->t_oproc) { (*tp->t_oproc)(tp); if ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && ISSET(tp->t_state, TS_CONNECTED)) { SET(tp->t_state, TS_SO_OCOMPLETE); error = ttysleep(tp, TSA_OCOMPLETE(tp), TTOPRI | PCATCH, "ttywai", tp->t_timeout); if (error) { if (error == EWOULDBLOCK) error = EIO; break; } } else break; } if (!error && (tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY))) error = EIO; splx(s); return (error); } /* * Flush if successfully wait. */ static int ttywflush(struct tty *tp) { int error; if ((error = ttywait(tp)) == 0) ttyflush(tp, FREAD); return (error); } /* * Flush tty read and/or write queues, notifying anyone waiting. */ void ttyflush(struct tty *tp, int rw) { int s; s = spltty(); #if 0 again: #endif if (rw & FWRITE) { FLUSHQ(&tp->t_outq); CLR(tp->t_state, TS_TTSTOP); } (*tp->t_stop)(tp, rw); if (rw & FREAD) { FLUSHQ(&tp->t_canq); FLUSHQ(&tp->t_rawq); CLR(tp->t_lflag, PENDIN); tp->t_rocount = 0; tp->t_rocol = 0; CLR(tp->t_state, TS_LOCAL); ttwakeup(tp); if (ISSET(tp->t_state, TS_TBLOCK)) { if (rw & FWRITE) FLUSHQ(&tp->t_outq); ttyunblock(tp); /* * Don't let leave any state that might clobber the * next line discipline (although we should do more * to send the START char). Not clearing the state * may have caused the "putc to a clist with no * reserved cblocks" panic/printf. */ CLR(tp->t_state, TS_TBLOCK); #if 0 /* forget it, sleeping isn't always safe and we don't know when it is */ if (ISSET(tp->t_iflag, IXOFF)) { /* * XXX wait a bit in the hope that the stop * character (if any) will go out. Waiting * isn't good since it allows races. This * will be fixed when the stop character is * put in a special queue. Don't bother with * the checks in ttywait() since the timeout * will save us. */ SET(tp->t_state, TS_SO_OCOMPLETE); ttysleep(tp, TSA_OCOMPLETE(tp), TTOPRI, "ttyfls", hz / 10); /* * Don't try sending the stop character again. */ CLR(tp->t_state, TS_TBLOCK); goto again; } #endif } } if (rw & FWRITE) { FLUSHQ(&tp->t_outq); ttwwakeup(tp); } splx(s); } /* * Copy in the default termios characters. */ void termioschars(struct termios *t) { bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); } /* * Old interface. */ void ttychars(struct tty *tp) { termioschars(&tp->t_termios); } /* * Handle input high water. Send stop character for the IXOFF case. Turn * on our input flow control bit and propagate the changes to the driver. * XXX the stop character should be put in a special high priority queue. */ void ttyblock(struct tty *tp) { SET(tp->t_state, TS_TBLOCK); if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE && putc(tp->t_cc[VSTOP], &tp->t_outq) != 0) CLR(tp->t_state, TS_TBLOCK); /* try again later */ ttstart(tp); } /* * Handle input low water. Send start character for the IXOFF case. Turn * off our input flow control bit and propagate the changes to the driver. * XXX the start character should be put in a special high priority queue. */ static void ttyunblock(struct tty *tp) { CLR(tp->t_state, TS_TBLOCK); if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTART] != _POSIX_VDISABLE && putc(tp->t_cc[VSTART], &tp->t_outq) != 0) SET(tp->t_state, TS_TBLOCK); /* try again later */ ttstart(tp); } #ifdef notyet /* Not used by any current (i386) drivers. */ /* * Restart after an inter-char delay. */ void ttrstrt(void *tp_arg) { struct tty *tp; int s; KASSERT(tp_arg != NULL, ("ttrstrt")); tp = tp_arg; s = spltty(); CLR(tp->t_state, TS_TIMEOUT); ttstart(tp); splx(s); } #endif int ttstart(struct tty *tp) { if (tp->t_oproc != NULL) /* XXX: Kludge for pty. */ (*tp->t_oproc)(tp); return (0); } /* * "close" a line discipline */ int ttylclose(struct tty *tp, int flag) { if (flag & FNONBLOCK || ttywflush(tp)) ttyflush(tp, FREAD | FWRITE); return (0); } /* * Handle modem control transition on a tty. * Flag indicates new state of carrier. * Returns 0 if the line should be turned off, otherwise 1. */ int ttymodem(struct tty *tp, int flag) { if (ISSET(tp->t_state, TS_CARR_ON) && ISSET(tp->t_cflag, MDMBUF)) { /* * MDMBUF: do flow control according to carrier flag * XXX TS_CAR_OFLOW doesn't do anything yet. TS_TTSTOP * works if IXON and IXANY are clear. */ if (flag) { CLR(tp->t_state, TS_CAR_OFLOW); CLR(tp->t_state, TS_TTSTOP); ttstart(tp); } else if (!ISSET(tp->t_state, TS_CAR_OFLOW)) { SET(tp->t_state, TS_CAR_OFLOW); SET(tp->t_state, TS_TTSTOP); (*tp->t_stop)(tp, 0); } } else if (flag == 0) { /* * Lost carrier. */ CLR(tp->t_state, TS_CARR_ON); if (ISSET(tp->t_state, TS_ISOPEN) && !ISSET(tp->t_cflag, CLOCAL)) { SET(tp->t_state, TS_ZOMBIE); CLR(tp->t_state, TS_CONNECTED); if (tp->t_session) { sx_slock(&proctree_lock); if (tp->t_session->s_leader) { struct proc *p; p = tp->t_session->s_leader; PROC_LOCK(p); psignal(p, SIGHUP); PROC_UNLOCK(p); } sx_sunlock(&proctree_lock); } ttyflush(tp, FREAD | FWRITE); return (0); } } else { /* * Carrier now on. */ SET(tp->t_state, TS_CARR_ON); if (!ISSET(tp->t_state, TS_ZOMBIE)) SET(tp->t_state, TS_CONNECTED); wakeup(TSA_CARR_ON(tp)); ttwakeup(tp); ttwwakeup(tp); } return (1); } /* * Reinput pending characters after state switch * call at spltty(). */ static void ttypend(struct tty *tp) { struct clist tq; int c; CLR(tp->t_lflag, PENDIN); SET(tp->t_state, TS_TYPEN); /* * XXX this assumes too much about clist internals. It may even * fail if the cblock slush pool is empty. We can't allocate more * cblocks here because we are called from an interrupt handler * and clist_alloc_cblocks() can wait. */ tq = tp->t_rawq; bzero(&tp->t_rawq, sizeof tp->t_rawq); tp->t_rawq.c_cbmax = tq.c_cbmax; tp->t_rawq.c_cbreserved = tq.c_cbreserved; while ((c = getc(&tq)) >= 0) ttyinput(c, tp); CLR(tp->t_state, TS_TYPEN); } /* * Process a read call on a tty device. */ int ttread(struct tty *tp, struct uio *uio, int flag) { struct clist *qp; int c; tcflag_t lflag; cc_t *cc = tp->t_cc; struct thread *td; struct proc *p; int s, first, error = 0; int has_stime = 0, last_cc = 0; long slp = 0; /* XXX this should be renamed `timo'. */ struct timeval stime; struct pgrp *pg; td = curthread; p = td->td_proc; loop: s = spltty(); lflag = tp->t_lflag; /* * take pending input first */ if (ISSET(lflag, PENDIN)) { ttypend(tp); splx(s); /* reduce latency */ s = spltty(); lflag = tp->t_lflag; /* XXX ttypend() clobbers it */ } /* * Hang process if it's in the background. */ if (isbackground(p, tp)) { splx(s); sx_slock(&proctree_lock); PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTIN) || SIGISMEMBER(td->td_sigmask, SIGTTIN) || (p->p_flag & P_PPWAIT) || p->p_pgrp->pg_jobc == 0) { PROC_UNLOCK(p); sx_sunlock(&proctree_lock); return (EIO); } pg = p->p_pgrp; PROC_UNLOCK(p); PGRP_LOCK(pg); sx_sunlock(&proctree_lock); pgsignal(pg, SIGTTIN, 1); PGRP_UNLOCK(pg); error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg2", 0); if (error) return (error); goto loop; } if (ISSET(tp->t_state, TS_ZOMBIE)) { splx(s); return (0); /* EOF */ } /* * If canonical, use the canonical queue, * else use the raw queue. * * (should get rid of clists...) */ qp = ISSET(lflag, ICANON) ? &tp->t_canq : &tp->t_rawq; if (flag & IO_NDELAY) { if (qp->c_cc > 0) goto read; if (!ISSET(lflag, ICANON) && cc[VMIN] == 0) { splx(s); return (0); } splx(s); return (EWOULDBLOCK); } if (!ISSET(lflag, ICANON)) { int m = cc[VMIN]; long t = cc[VTIME]; struct timeval timecopy; /* * Check each of the four combinations. * (m > 0 && t == 0) is the normal read case. * It should be fairly efficient, so we check that and its * companion case (m == 0 && t == 0) first. * For the other two cases, we compute the target sleep time * into slp. */ if (t == 0) { if (qp->c_cc < m) goto sleep; if (qp->c_cc > 0) goto read; /* m, t and qp->c_cc are all 0. 0 is enough input. */ splx(s); return (0); } t *= 100000; /* time in us */ #define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \ ((t1).tv_usec - (t2).tv_usec)) if (m > 0) { if (qp->c_cc <= 0) goto sleep; if (qp->c_cc >= m) goto read; getmicrotime(&timecopy); if (!has_stime) { /* first character, start timer */ has_stime = 1; stime = timecopy; slp = t; } else if (qp->c_cc > last_cc) { /* got a character, restart timer */ stime = timecopy; slp = t; } else { /* nothing, check expiration */ slp = t - diff(timecopy, stime); if (slp <= 0) goto read; } last_cc = qp->c_cc; } else { /* m == 0 */ if (qp->c_cc > 0) goto read; getmicrotime(&timecopy); if (!has_stime) { has_stime = 1; stime = timecopy; slp = t; } else { slp = t - diff(timecopy, stime); if (slp <= 0) { /* Timed out, but 0 is enough input. */ splx(s); return (0); } } } #undef diff /* * Rounding down may make us wake up just short * of the target, so we round up. * The formula is ceiling(slp * hz/1000000). * 32-bit arithmetic is enough for hz < 169. * XXX see tvtohz() for how to avoid overflow if hz * is large (divide by `tick' and/or arrange to * use tvtohz() if hz is large). */ slp = (long) (((u_long)slp * hz) + 999999) / 1000000; goto sleep; } if (qp->c_cc <= 0) { sleep: /* * There is no input, or not enough input and we can block. */ error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH, ISSET(tp->t_state, TS_CONNECTED) ? "ttyin" : "ttyhup", (int)slp); splx(s); if (error == EWOULDBLOCK) error = 0; else if (error) return (error); /* * XXX what happens if another process eats some input * while we are asleep (not just here)? It would be * safest to detect changes and reset our state variables * (has_stime and last_cc). */ slp = 0; goto loop; } read: splx(s); /* * Input present, check for input mapping and processing. */ first = 1; if (ISSET(lflag, ICANON | ISIG)) goto slowcase; for (;;) { char ibuf[IBUFSIZ]; int icc; icc = imin(uio->uio_resid, IBUFSIZ); icc = q_to_b(qp, ibuf, icc); if (icc <= 0) { if (first) goto loop; break; } error = uiomove(ibuf, icc, uio); /* * XXX if there was an error then we should ungetc() the * unmoved chars and reduce icc here. */ if (error) break; if (uio->uio_resid == 0) break; first = 0; } goto out; slowcase: for (;;) { c = getc(qp); if (c < 0) { if (first) goto loop; break; } /* * delayed suspend (^Y) */ if (CCEQ(cc[VDSUSP], c) && ISSET(lflag, IEXTEN | ISIG) == (IEXTEN | ISIG)) { if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGTSTP, 1); PGRP_UNLOCK(tp->t_pgrp); } if (first) { error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg3", 0); if (error) break; goto loop; } break; } /* * Interpret EOF only in canonical mode. */ if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON)) break; /* * Give user character. */ error = ureadc(c, uio); if (error) /* XXX should ungetc(c, qp). */ break; if (uio->uio_resid == 0) break; /* * In canonical mode check for a "break character" * marking the end of a "line of input". */ if (ISSET(lflag, ICANON) && TTBREAKC(c, lflag)) break; first = 0; } out: /* * Look to unblock input now that (presumably) * the input queue has gone down. */ s = spltty(); if (ISSET(tp->t_state, TS_TBLOCK) && tp->t_rawq.c_cc + tp->t_canq.c_cc <= tp->t_ilowat) ttyunblock(tp); splx(s); return (error); } /* * Check the output queue on tp for space for a kernel message (from uprintf * or tprintf). Allow some space over the normal hiwater mark so we don't * lose messages due to normal flow control, but don't let the tty run amok. * Sleeps here are not interruptible, but we return prematurely if new signals * arrive. */ int ttycheckoutq(struct tty *tp, int wait) { int hiwat, s; sigset_t oldmask; struct thread *td; struct proc *p; td = curthread; p = td->td_proc; hiwat = tp->t_ohiwat; SIGEMPTYSET(oldmask); s = spltty(); if (wait) { PROC_LOCK(p); oldmask = td->td_siglist; PROC_UNLOCK(p); } if (tp->t_outq.c_cc > hiwat + OBUFSIZ + 100) while (tp->t_outq.c_cc > hiwat) { ttstart(tp); if (tp->t_outq.c_cc <= hiwat) break; if (!wait) { splx(s); return (0); } PROC_LOCK(p); if (!SIGSETEQ(td->td_siglist, oldmask)) { PROC_UNLOCK(p); splx(s); return (0); } PROC_UNLOCK(p); SET(tp->t_state, TS_SO_OLOWAT); tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttoutq", hz); } splx(s); return (1); } /* * Process a write call on a tty device. */ int ttwrite(struct tty *tp, struct uio *uio, int flag) { char *cp = NULL; int cc, ce; struct thread *td; struct proc *p; int i, hiwat, cnt, error, s; char obuf[OBUFSIZ]; hiwat = tp->t_ohiwat; cnt = uio->uio_resid; error = 0; cc = 0; td = curthread; p = td->td_proc; loop: s = spltty(); if (ISSET(tp->t_state, TS_ZOMBIE)) { splx(s); if (uio->uio_resid == cnt) error = EIO; goto out; } if (!ISSET(tp->t_state, TS_CONNECTED)) { if (flag & IO_NDELAY) { splx(s); error = EWOULDBLOCK; goto out; } error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, "ttydcd", 0); splx(s); if (error) goto out; goto loop; } splx(s); /* * Hang the process if it's in the background. */ sx_slock(&proctree_lock); PROC_LOCK(p); if (isbackground(p, tp) && ISSET(tp->t_lflag, TOSTOP) && !(p->p_flag & P_PPWAIT) && !SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTOU) && !SIGISMEMBER(td->td_sigmask, SIGTTOU)) { if (p->p_pgrp->pg_jobc == 0) { PROC_UNLOCK(p); sx_sunlock(&proctree_lock); error = EIO; goto out; } PROC_UNLOCK(p); PGRP_LOCK(p->p_pgrp); sx_sunlock(&proctree_lock); pgsignal(p->p_pgrp, SIGTTOU, 1); PGRP_UNLOCK(p->p_pgrp); error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg4", 0); if (error) goto out; goto loop; } else { PROC_UNLOCK(p); sx_sunlock(&proctree_lock); } /* * Process the user's data in at most OBUFSIZ chunks. Perform any * output translation. Keep track of high water mark, sleep on * overflow awaiting device aid in acquiring new space. */ while (uio->uio_resid > 0 || cc > 0) { if (ISSET(tp->t_lflag, FLUSHO)) { uio->uio_resid = 0; return (0); } if (tp->t_outq.c_cc > hiwat) goto ovhiwat; /* * Grab a hunk of data from the user, unless we have some * leftover from last time. */ if (cc == 0) { cc = imin(uio->uio_resid, OBUFSIZ); cp = obuf; error = uiomove(cp, cc, uio); if (error) { cc = 0; break; } } /* * If nothing fancy need be done, grab those characters we * can handle without any of ttyoutput's processing and * just transfer them to the output q. For those chars * which require special processing (as indicated by the * bits in char_type), call ttyoutput. After processing * a hunk of data, look for FLUSHO so ^O's will take effect * immediately. */ while (cc > 0) { if (!ISSET(tp->t_oflag, OPOST)) ce = cc; else { ce = cc - scanc((u_int)cc, (u_char *)cp, char_type, CCLASSMASK); /* * If ce is zero, then we're processing * a special character through ttyoutput. */ if (ce == 0) { tp->t_rocount = 0; if (ttyoutput(*cp, tp) >= 0) { /* No Clists, wait a bit. */ ttstart(tp); if (flag & IO_NDELAY) { error = EWOULDBLOCK; goto out; } error = ttysleep(tp, &lbolt, TTOPRI|PCATCH, "ttybf1", 0); if (error) goto out; goto loop; } cp++; cc--; if (ISSET(tp->t_lflag, FLUSHO) || tp->t_outq.c_cc > hiwat) goto ovhiwat; continue; } } /* * A bunch of normal characters have been found. * Transfer them en masse to the output queue and * continue processing at the top of the loop. * If there are any further characters in this * <= OBUFSIZ chunk, the first should be a character * requiring special handling by ttyoutput. */ tp->t_rocount = 0; i = b_to_q(cp, ce, &tp->t_outq); ce -= i; tp->t_column += ce; cp += ce, cc -= ce, tk_nout += ce; tp->t_outcc += ce; if (i > 0) { /* No Clists, wait a bit. */ ttstart(tp); if (flag & IO_NDELAY) { error = EWOULDBLOCK; goto out; } error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, "ttybf2", 0); if (error) goto out; goto loop; } if (ISSET(tp->t_lflag, FLUSHO) || tp->t_outq.c_cc > hiwat) break; } ttstart(tp); } out: /* * If cc is nonzero, we leave the uio structure inconsistent, as the * offset and iov pointers have moved forward, but it doesn't matter * (the call will either return short or restart with a new uio). */ uio->uio_resid += cc; return (error); ovhiwat: ttstart(tp); s = spltty(); /* * This can only occur if FLUSHO is set in t_lflag, * or if ttstart/oproc is synchronous (or very fast). */ if (tp->t_outq.c_cc <= hiwat) { splx(s); goto loop; } if (flag & IO_NDELAY) { splx(s); uio->uio_resid += cc; return (uio->uio_resid == cnt ? EWOULDBLOCK : 0); } SET(tp->t_state, TS_SO_OLOWAT); error = ttysleep(tp, TSA_OLOWAT(tp), TTOPRI | PCATCH, "ttywri", tp->t_timeout); splx(s); if (error == EWOULDBLOCK) error = EIO; if (error) goto out; goto loop; } /* * Rubout one character from the rawq of tp * as cleanly as possible. */ static void ttyrub(int c, struct tty *tp) { char *cp; int savecol; int tabc, s; if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC)) return; CLR(tp->t_lflag, FLUSHO); if (ISSET(tp->t_lflag, ECHOE)) { if (tp->t_rocount == 0) { /* * Screwed by ttwrite; retype */ ttyretype(tp); return; } if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE)) ttyrubo(tp, 2); else { CLR(c, ~TTY_CHARMASK); switch (CCLASS(c)) { case ORDINARY: ttyrubo(tp, 1); break; case BACKSPACE: case CONTROL: case NEWLINE: case RETURN: case VTAB: if (ISSET(tp->t_lflag, ECHOCTL)) ttyrubo(tp, 2); break; case TAB: if (tp->t_rocount < tp->t_rawq.c_cc) { ttyretype(tp); return; } s = spltty(); savecol = tp->t_column; SET(tp->t_state, TS_CNTTB); SET(tp->t_lflag, FLUSHO); tp->t_column = tp->t_rocol; cp = tp->t_rawq.c_cf; if (cp) tabc = *cp; /* XXX FIX NEXTC */ for (; cp; cp = nextc(&tp->t_rawq, cp, &tabc)) ttyecho(tabc, tp); CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_CNTTB); splx(s); /* savecol will now be length of the tab. */ savecol -= tp->t_column; tp->t_column += savecol; if (savecol > 8) savecol = 8; /* overflow screw */ while (--savecol >= 0) (void)ttyoutput('\b', tp); break; default: /* XXX */ #define PANICSTR "ttyrub: would panic c = %d, val = %d\n" (void)printf(PANICSTR, c, CCLASS(c)); #ifdef notdef panic(PANICSTR, c, CCLASS(c)); #endif } } } else if (ISSET(tp->t_lflag, ECHOPRT)) { if (!ISSET(tp->t_state, TS_ERASE)) { SET(tp->t_state, TS_ERASE); (void)ttyoutput('\\', tp); } ttyecho(c, tp); } else { ttyecho(tp->t_cc[VERASE], tp); /* * This code may be executed not only when an ERASE key * is pressed, but also when ^U (KILL) or ^W (WERASE) are. * So, I didn't think it was worthwhile to pass the extra * information (which would need an extra parameter, * changing every call) needed to distinguish the ERASE2 * case from the ERASE. */ } --tp->t_rocount; } /* * Back over cnt characters, erasing them. */ static void ttyrubo(struct tty *tp, int cnt) { while (cnt-- > 0) { (void)ttyoutput('\b', tp); (void)ttyoutput(' ', tp); (void)ttyoutput('\b', tp); } } /* * ttyretype -- * Reprint the rawq line. Note, it is assumed that c_cc has already * been checked. */ static void ttyretype(struct tty *tp) { char *cp; int s, c; /* Echo the reprint character. */ if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE) ttyecho(tp->t_cc[VREPRINT], tp); (void)ttyoutput('\n', tp); /* * XXX * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE * BIT OF FIRST CHAR. */ s = spltty(); for (cp = tp->t_canq.c_cf, c = (cp != NULL ? *cp : 0); cp != NULL; cp = nextc(&tp->t_canq, cp, &c)) ttyecho(c, tp); for (cp = tp->t_rawq.c_cf, c = (cp != NULL ? *cp : 0); cp != NULL; cp = nextc(&tp->t_rawq, cp, &c)) ttyecho(c, tp); CLR(tp->t_state, TS_ERASE); splx(s); tp->t_rocount = tp->t_rawq.c_cc; tp->t_rocol = 0; } /* * Echo a typed character to the terminal. */ static void ttyecho(int c, struct tty *tp) { if (!ISSET(tp->t_state, TS_CNTTB)) CLR(tp->t_lflag, FLUSHO); if ((!ISSET(tp->t_lflag, ECHO) && (c != '\n' || !ISSET(tp->t_lflag, ECHONL))) || ISSET(tp->t_lflag, EXTPROC)) return; if (ISSET(tp->t_lflag, ECHOCTL) && ((ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n') || ISSET(c, TTY_CHARMASK) == 0177)) { (void)ttyoutput('^', tp); CLR(c, ~TTY_CHARMASK); if (c == 0177) c = '?'; else c += 'A' - 1; } (void)ttyoutput(c, tp); } /* * Wake up any readers on a tty. */ void ttwakeup(struct tty *tp) { if (SEL_WAITING(&tp->t_rsel)) selwakeuppri(&tp->t_rsel, TTIPRI); if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); wakeup(TSA_HUP_OR_INPUT(tp)); KNOTE(&tp->t_rsel.si_note, 0); } /* * Wake up any writers on a tty. */ void ttwwakeup(struct tty *tp) { if (SEL_WAITING(&tp->t_wsel) && tp->t_outq.c_cc <= tp->t_olowat) selwakeuppri(&tp->t_wsel, TTOPRI); if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) == TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) { CLR(tp->t_state, TS_SO_OCOMPLETE); wakeup(TSA_OCOMPLETE(tp)); } if (ISSET(tp->t_state, TS_SO_OLOWAT) && tp->t_outq.c_cc <= tp->t_olowat) { CLR(tp->t_state, TS_SO_OLOWAT); wakeup(TSA_OLOWAT(tp)); } KNOTE(&tp->t_wsel.si_note, 0); } /* * Look up a code for a specified speed in a conversion table; * used by drivers to map software speed values to hardware parameters. */ int ttspeedtab(int speed, struct speedtab *table) { for ( ; table->sp_speed != -1; table++) if (table->sp_speed == speed) return (table->sp_code); return (-1); } /* * Set input and output watermarks and buffer sizes. For input, the * high watermark is about one second's worth of input above empty, the * low watermark is slightly below high water, and the buffer size is a * driver-dependent amount above high water. For output, the watermarks * are near the ends of the buffer, with about 1 second's worth of input * between them. All this only applies to the standard line discipline. */ void ttsetwater(struct tty *tp) { int cps, ttmaxhiwat, x; /* Input. */ clist_alloc_cblocks(&tp->t_canq, TTYHOG, 512); switch (tp->t_ispeedwat) { case (speed_t)-1: cps = tp->t_ispeed / 10; break; case 0: /* * This case is for old drivers that don't know about * t_ispeedwat. Arrange for them to get the old buffer * sizes and watermarks. */ cps = TTYHOG - 2 * 256; tp->t_ififosize = 2 * 256; break; default: cps = tp->t_ispeedwat / 10; break; } tp->t_ihiwat = cps; tp->t_ilowat = 7 * cps / 8; x = cps + tp->t_ififosize; clist_alloc_cblocks(&tp->t_rawq, x, x); /* Output. */ switch (tp->t_ospeedwat) { case (speed_t)-1: cps = tp->t_ospeed / 10; ttmaxhiwat = 2 * TTMAXHIWAT; break; case 0: cps = tp->t_ospeed / 10; ttmaxhiwat = TTMAXHIWAT; break; default: cps = tp->t_ospeedwat / 10; ttmaxhiwat = 8 * TTMAXHIWAT; break; } #define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x)) tp->t_olowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT); x += cps; x = CLAMP(x, ttmaxhiwat, TTMINHIWAT); /* XXX clamps are too magic */ tp->t_ohiwat = roundup(x, CBSIZE); /* XXX for compat */ x = imax(tp->t_ohiwat, TTMAXHIWAT); /* XXX for compat/safety */ x += OBUFSIZ + 100; clist_alloc_cblocks(&tp->t_outq, x, x); #undef CLAMP } /* * Report on state of foreground process group. */ void ttyinfo(struct tty *tp) { struct timeval utime, stime; struct proc *p, *pick; struct thread *td; const char *stateprefix, *state; long rss; int load, pctcpu; if (ttycheckoutq(tp,0) == 0) return; /* Print load average. */ load = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT; ttyprintf(tp, "load: %d.%02d ", load / 100, load % 100); /* * On return following a ttyprintf(), we set tp->t_rocount to 0 so * that pending input will be retyped on BS. */ if (tp->t_session == NULL) { ttyprintf(tp, "not a controlling terminal\n"); tp->t_rocount = 0; return; } if (tp->t_pgrp == NULL) { ttyprintf(tp, "no foreground process group\n"); tp->t_rocount = 0; return; } PGRP_LOCK(tp->t_pgrp); if ((p = LIST_FIRST(&tp->t_pgrp->pg_members)) == 0) { PGRP_UNLOCK(tp->t_pgrp); ttyprintf(tp, "empty foreground process group\n"); tp->t_rocount = 0; return; } /* * Pick the most interesting process and copy some of its * state for printing later. sched_lock must be held for * most parts of this. Holding it throughout is simplest * and prevents even unimportant inconsistencies in the * copy of the state, but may increase interrupt latency * too much. */ mtx_lock_spin(&sched_lock); for (pick = NULL; p != 0; p = LIST_NEXT(p, p_pglist)) if (proc_compare(pick, p)) pick = p; PGRP_UNLOCK(tp->t_pgrp); td = FIRST_THREAD_IN_PROC(pick); /* XXXKSE */ #if 0 KASSERT(td != NULL, ("ttyinfo: no thread")); #else if (td == NULL) { mtx_unlock_spin(&sched_lock); ttyprintf(tp, "foreground process without thread\n"); tp->t_rocount = 0; return; } #endif stateprefix = ""; if (TD_IS_RUNNING(td)) state = "running"; else if (TD_ON_RUNQ(td) || TD_CAN_RUN(td)) state = "runnable"; else if (TD_IS_SLEEPING(td)) { /* XXX: If we're sleeping, are we ever not in a queue? */ if (TD_ON_SLEEPQ(td)) state = td->td_wmesg; else state = "sleeping without queue"; } else if (TD_ON_LOCK(td)) { state = td->td_lockname; stateprefix = "*"; } else if (TD_IS_SUSPENDED(td)) state = "suspended"; else if (TD_AWAITING_INTR(td)) state = "intrwait"; else state = "unknown"; calcru(pick, &utime, &stime, NULL); pctcpu = (sched_pctcpu(td) * 10000 + FSCALE / 2) >> FSHIFT; if (pick->p_state == PRS_NEW || pick->p_state == PRS_ZOMBIE) rss = 0; else rss = pgtok(vmspace_resident_count(pick->p_vmspace)); mtx_unlock_spin(&sched_lock); /* Print command, pid, state, utime, stime, %cpu, and rss. */ ttyprintf(tp, " cmd: %s %d [%s%s] %ld.%02ldu %ld.%02lds %d%% %ldk\n", pick->p_comm, pick->p_pid, stateprefix, state, (long)utime.tv_sec, utime.tv_usec / 10000, (long)stime.tv_sec, stime.tv_usec / 10000, pctcpu / 100, rss); tp->t_rocount = 0; } /* * Returns 1 if p2 is "better" than p1 * * The algorithm for picking the "interesting" process is thus: * * 1) Only foreground processes are eligible - implied. * 2) Runnable processes are favored over anything else. The runner * with the highest cpu utilization is picked (p_estcpu). Ties are * broken by picking the highest pid. * 3) The sleeper with the shortest sleep time is next. With ties, * we pick out just "short-term" sleepers (P_SINTR == 0). * 4) Further ties are broken by picking the highest pid. */ #define ISRUN(p, val) \ do { \ struct thread *td; \ val = 0; \ FOREACH_THREAD_IN_PROC(p, td) { \ if (TD_ON_RUNQ(td) || \ TD_IS_RUNNING(td)) { \ val = 1; \ break; \ } \ } \ } while (0) #define TESTAB(a, b) ((a)<<1 | (b)) #define ONLYA 2 #define ONLYB 1 #define BOTH 3 static int proc_compare(struct proc *p1, struct proc *p2) { int esta, estb; struct ksegrp *kg; mtx_assert(&sched_lock, MA_OWNED); if (p1 == NULL) return (1); ISRUN(p1, esta); ISRUN(p2, estb); /* * see if at least one of them is runnable */ switch (TESTAB(esta, estb)) { case ONLYA: return (0); case ONLYB: return (1); case BOTH: /* * tie - favor one with highest recent cpu utilization */ esta = estb = 0; FOREACH_KSEGRP_IN_PROC(p1,kg) { esta += kg->kg_estcpu; } FOREACH_KSEGRP_IN_PROC(p2,kg) { estb += kg->kg_estcpu; } if (estb > esta) return (1); if (esta > estb) return (0); return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * weed out zombies */ switch (TESTAB(p1->p_state == PRS_ZOMBIE, p2->p_state == PRS_ZOMBIE)) { case ONLYA: return (1); case ONLYB: return (0); case BOTH: return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } #if 0 /* XXXKSE */ /* * pick the one with the smallest sleep time */ if (p2->p_slptime > p1->p_slptime) return (0); if (p1->p_slptime > p2->p_slptime) return (1); /* * favor one sleeping in a non-interruptible sleep */ if (p1->p_sflag & PS_SINTR && (p2->p_sflag & PS_SINTR) == 0) return (1); if (p2->p_sflag & PS_SINTR && (p1->p_sflag & PS_SINTR) == 0) return (0); #endif return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * Output char to tty; console putchar style. */ int tputchar(int c, struct tty *tp) { int s; s = spltty(); if (!ISSET(tp->t_state, TS_CONNECTED)) { splx(s); return (-1); } if (c == '\n') (void)ttyoutput('\r', tp); (void)ttyoutput(c, tp); ttstart(tp); splx(s); return (0); } /* * Sleep on chan, returning ERESTART if tty changed while we napped and * returning any errors (e.g. EINTR/EWOULDBLOCK) reported by tsleep. If * the tty is revoked, restarting a pending call will redo validation done * at the start of the call. */ int ttysleep(struct tty *tp, void *chan, int pri, char *wmesg, int timo) { int error; int gen; gen = tp->t_gen; error = tsleep(chan, pri, wmesg, timo); if (error) return (error); return (tp->t_gen == gen ? 0 : ERESTART); } /* * Gain a reference to a TTY */ int ttyref(struct tty *tp) { int i; mtx_lock(&tp->t_mtx); KASSERT(tp->t_refcnt > 0, ("ttyref(): tty refcnt is %d (%s)", tp->t_refcnt, tp->t_dev != NULL ? devtoname(tp->t_dev) : "??")); i = ++tp->t_refcnt; mtx_unlock(&tp->t_mtx); return (i); } /* * Drop a reference to a TTY. * When reference count drops to zero, we free it. */ int ttyrel(struct tty *tp) { int i; mtx_lock(&tty_list_mutex); mtx_lock(&tp->t_mtx); KASSERT(tp->t_refcnt > 0, ("ttyrel(): tty refcnt is %d (%s)", tp->t_refcnt, tp->t_dev != NULL ? devtoname(tp->t_dev) : "??")); i = --tp->t_refcnt; if (i != 0) { mtx_unlock(&tp->t_mtx); mtx_unlock(&tty_list_mutex); return (i); } TAILQ_REMOVE(&tty_list, tp, t_list); mtx_unlock(&tp->t_mtx); mtx_unlock(&tty_list_mutex); mtx_destroy(&tp->t_mtx); free(tp, M_TTYS); return (i); } /* * Allocate a tty struct. Clists in the struct will be allocated by * ttyopen(). */ struct tty * ttymalloc(struct tty *tp) { static int once; if (!once) { mtx_init(&tty_list_mutex, "ttylist", NULL, MTX_DEF); once++; } if (tp) { /* * XXX: Either this argument should go away, or we should * XXX: require it and do a ttyrel(tp) here and allocate * XXX: a new tty. For now do nothing. */ return(tp); } tp = malloc(sizeof *tp, M_TTYS, M_WAITOK | M_ZERO); tp->t_timeout = -1; mtx_init(&tp->t_mtx, "tty", NULL, MTX_DEF); tp->t_refcnt = 1; mtx_lock(&tty_list_mutex); TAILQ_INSERT_TAIL(&tty_list, tp, t_list); mtx_unlock(&tty_list_mutex); return (tp); } static int sysctl_kern_ttys(SYSCTL_HANDLER_ARGS) { struct tty *tp, *tp2; struct xtty xt; int error; error = 0; mtx_lock(&tty_list_mutex); tp = TAILQ_FIRST(&tty_list); if (tp != NULL) ttyref(tp); mtx_unlock(&tty_list_mutex); while (tp != NULL) { bzero(&xt, sizeof xt); xt.xt_size = sizeof xt; #define XT_COPY(field) xt.xt_##field = tp->t_##field xt.xt_rawcc = tp->t_rawq.c_cc; xt.xt_cancc = tp->t_canq.c_cc; xt.xt_outcc = tp->t_outq.c_cc; XT_COPY(line); if (tp->t_dev != NULL) xt.xt_dev = dev2udev(tp->t_dev); XT_COPY(state); XT_COPY(flags); XT_COPY(timeout); if (tp->t_pgrp != NULL) xt.xt_pgid = tp->t_pgrp->pg_id; if (tp->t_session != NULL) xt.xt_sid = tp->t_session->s_sid; XT_COPY(termios); XT_COPY(winsize); XT_COPY(column); XT_COPY(rocount); XT_COPY(rocol); XT_COPY(ififosize); XT_COPY(ihiwat); XT_COPY(ilowat); XT_COPY(ispeedwat); XT_COPY(ohiwat); XT_COPY(olowat); XT_COPY(ospeedwat); #undef XT_COPY error = SYSCTL_OUT(req, &xt, sizeof xt); if (error != 0) { ttyrel(tp); return (error); } mtx_lock(&tty_list_mutex); tp2 = TAILQ_NEXT(tp, t_list); if (tp2 != NULL) ttyref(tp2); mtx_unlock(&tty_list_mutex); ttyrel(tp); tp = tp2; } return (0); } SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_kern_ttys, "S,xtty", "All ttys"); SYSCTL_LONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, &tk_nin, 0, "Total TTY in characters"); SYSCTL_LONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, &tk_nout, 0, "Total TTY out characters"); void nottystop(struct tty *tp, int rw) { return; } int ttyread(dev_t dev, struct uio *uio, int flag) { struct tty *tp; KASSERT(devsw(dev)->d_flags & D_TTY, ("ttyread() called on non D_TTY device (%s)", devtoname(dev))); tp = dev->si_tty; KASSERT(tp != NULL, ("ttyread(): no tty pointer on device (%s)", devtoname(dev))); if (tp == NULL) return (ENODEV); return (ttyld_read(tp, uio, flag)); } int ttywrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp; KASSERT(devsw(dev)->d_flags & D_TTY, ("ttywrite() called on non D_TTY device (%s)", devtoname(dev))); tp = dev->si_tty; KASSERT(tp != NULL, ("ttywrite(): no tty pointer on device (%s)", devtoname(dev))); if (tp == NULL) return (ENODEV); return (ttyld_write(tp, uio, flag)); } int ttyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct tty *tp; int error; tp = dev->si_tty; error = ttyld_ioctl(tp, cmd, data, flag, td); if (error == ENOIOCTL) error = ttioctl(tp, cmd, data, flag); if (error != ENOIOCTL) return (error); return (ENOTTY); } int ttyldoptim(struct tty *tp) { struct termios *t; t = &tp->t_termios; if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line]->l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; return (linesw[tp->t_line]->l_hotchar); } Index: head/sys/kern/tty_compat.c =================================================================== --- head/sys/kern/tty_compat.c (revision 130343) +++ head/sys/kern/tty_compat.c (revision 130344) @@ -1,488 +1,488 @@ /*- * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * * 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. * 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. * * @(#)tty_compat.c 8.1 (Berkeley) 6/10/93 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" /* * mapping routines for old line discipline (yuck) */ -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #include #include #include #include #include #include static int ttcompatgetflags(struct tty *tp); static void ttcompatsetflags(struct tty *tp, struct termios *t); static void ttcompatsetlflags(struct tty *tp, struct termios *t); static int ttcompatspeedtab(int speed, struct speedtab *table); static int ttydebug = 0; SYSCTL_INT(_debug, OID_AUTO, ttydebug, CTLFLAG_RW, &ttydebug, 0, ""); static struct speedtab compatspeeds[] = { #define MAX_SPEED 17 { 115200, 17 }, { 57600, 16 }, { 38400, 15 }, { 19200, 14 }, { 9600, 13 }, { 4800, 12 }, { 2400, 11 }, { 1800, 10 }, { 1200, 9 }, { 600, 8 }, { 300, 7 }, { 200, 6 }, { 150, 5 }, { 134, 4 }, { 110, 3 }, { 75, 2 }, { 50, 1 }, { 0, 0 }, { -1, -1 }, }; static int compatspcodes[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, }; static int ttcompatspeedtab(speed, table) int speed; register struct speedtab *table; { if (speed == 0) return (0); /* hangup */ for ( ; table->sp_speed > 0; table++) if (table->sp_speed <= speed) /* nearest one, rounded down */ return (table->sp_code); return (1); /* 50, min and not hangup */ } int ttsetcompat(tp, com, data, term) register struct tty *tp; u_long *com; caddr_t data; struct termios *term; { switch (*com) { case TIOCSETP: case TIOCSETN: { register struct sgttyb *sg = (struct sgttyb *)data; int speed; if ((speed = sg->sg_ispeed) > MAX_SPEED || speed < 0) return(EINVAL); else if (speed != ttcompatspeedtab(tp->t_ispeed, compatspeeds)) term->c_ispeed = compatspcodes[speed]; else term->c_ispeed = tp->t_ispeed; if ((speed = sg->sg_ospeed) > MAX_SPEED || speed < 0) return(EINVAL); else if (speed != ttcompatspeedtab(tp->t_ospeed, compatspeeds)) term->c_ospeed = compatspcodes[speed]; else term->c_ospeed = tp->t_ospeed; term->c_cc[VERASE] = sg->sg_erase; term->c_cc[VKILL] = sg->sg_kill; tp->t_flags = (tp->t_flags&0xffff0000) | (sg->sg_flags&0xffff); ttcompatsetflags(tp, term); *com = (*com == TIOCSETP) ? TIOCSETAF : TIOCSETA; break; } case TIOCSETC: { struct tchars *tc = (struct tchars *)data; register cc_t *cc; cc = term->c_cc; cc[VINTR] = tc->t_intrc; cc[VQUIT] = tc->t_quitc; cc[VSTART] = tc->t_startc; cc[VSTOP] = tc->t_stopc; cc[VEOF] = tc->t_eofc; cc[VEOL] = tc->t_brkc; if (tc->t_brkc == (char)_POSIX_VDISABLE) cc[VEOL2] = _POSIX_VDISABLE; *com = TIOCSETA; break; } case TIOCSLTC: { struct ltchars *ltc = (struct ltchars *)data; register cc_t *cc; cc = term->c_cc; cc[VSUSP] = ltc->t_suspc; cc[VDSUSP] = ltc->t_dsuspc; cc[VREPRINT] = ltc->t_rprntc; cc[VDISCARD] = ltc->t_flushc; cc[VWERASE] = ltc->t_werasc; cc[VLNEXT] = ltc->t_lnextc; *com = TIOCSETA; break; } case TIOCLBIS: case TIOCLBIC: case TIOCLSET: if (*com == TIOCLSET) tp->t_flags = (tp->t_flags&0xffff) | *(int *)data<<16; else { tp->t_flags = (ttcompatgetflags(tp)&0xffff0000)|(tp->t_flags&0xffff); if (*com == TIOCLBIS) tp->t_flags |= *(int *)data<<16; else tp->t_flags &= ~(*(int *)data<<16); } ttcompatsetlflags(tp, term); *com = TIOCSETA; break; } return 0; } /*ARGSUSED*/ int ttcompat(tp, com, data, flag) register struct tty *tp; u_long com; caddr_t data; int flag; { switch (com) { case TIOCSETP: case TIOCSETN: case TIOCSETC: case TIOCSLTC: case TIOCLBIS: case TIOCLBIC: case TIOCLSET: { struct termios term; int error; term = tp->t_termios; if ((error = ttsetcompat(tp, &com, data, &term)) != 0) return error; return ttioctl(tp, com, &term, flag); } case TIOCGETP: { register struct sgttyb *sg = (struct sgttyb *)data; register cc_t *cc = tp->t_cc; sg->sg_ospeed = ttcompatspeedtab(tp->t_ospeed, compatspeeds); if (tp->t_ispeed == 0) sg->sg_ispeed = sg->sg_ospeed; else sg->sg_ispeed = ttcompatspeedtab(tp->t_ispeed, compatspeeds); sg->sg_erase = cc[VERASE]; sg->sg_kill = cc[VKILL]; sg->sg_flags = tp->t_flags = ttcompatgetflags(tp); break; } case TIOCGETC: { struct tchars *tc = (struct tchars *)data; register cc_t *cc = tp->t_cc; tc->t_intrc = cc[VINTR]; tc->t_quitc = cc[VQUIT]; tc->t_startc = cc[VSTART]; tc->t_stopc = cc[VSTOP]; tc->t_eofc = cc[VEOF]; tc->t_brkc = cc[VEOL]; break; } case TIOCGLTC: { struct ltchars *ltc = (struct ltchars *)data; register cc_t *cc = tp->t_cc; ltc->t_suspc = cc[VSUSP]; ltc->t_dsuspc = cc[VDSUSP]; ltc->t_rprntc = cc[VREPRINT]; ltc->t_flushc = cc[VDISCARD]; ltc->t_werasc = cc[VWERASE]; ltc->t_lnextc = cc[VLNEXT]; break; } case TIOCLGET: tp->t_flags = (ttcompatgetflags(tp) & 0xffff0000UL) | (tp->t_flags & 0xffff); *(int *)data = tp->t_flags>>16; if (ttydebug) printf("CLGET: returning %x\n", *(int *)data); break; case OTIOCGETD: *(int *)data = tp->t_line ? tp->t_line : 2; break; case OTIOCSETD: { int ldisczero = 0; return (ttioctl(tp, TIOCSETD, *(int *)data == 2 ? (caddr_t)&ldisczero : data, flag)); } case OTIOCCONS: *(int *)data = 1; return (ttioctl(tp, TIOCCONS, data, flag)); default: return (ENOIOCTL); } return (0); } static int ttcompatgetflags(tp) register struct tty *tp; { register tcflag_t iflag = tp->t_iflag; register tcflag_t lflag = tp->t_lflag; register tcflag_t oflag = tp->t_oflag; register tcflag_t cflag = tp->t_cflag; register int flags = 0; if (iflag&IXOFF) flags |= TANDEM; if (iflag&ICRNL || oflag&ONLCR) flags |= CRMOD; if ((cflag&CSIZE) == CS8) { flags |= PASS8; if (iflag&ISTRIP) flags |= ANYP; } else if (cflag&PARENB) { if (iflag&INPCK) { if (cflag&PARODD) flags |= ODDP; else flags |= EVENP; } else flags |= EVENP | ODDP; } if ((lflag&ICANON) == 0) { /* fudge */ if (iflag&(INPCK|ISTRIP|IXON) || lflag&(IEXTEN|ISIG) || (cflag&(CSIZE|PARENB)) != CS8) flags |= CBREAK; else flags |= RAW; } if (!(flags&RAW) && !(oflag&OPOST) && (cflag&(CSIZE|PARENB)) == CS8) flags |= LITOUT; if (cflag&MDMBUF) flags |= MDMBUF; if ((cflag&HUPCL) == 0) flags |= NOHANG; if (oflag&OXTABS) flags |= XTABS; if (lflag&ECHOE) flags |= CRTERA|CRTBS; if (lflag&ECHOKE) flags |= CRTKIL|CRTBS; if (lflag&ECHOPRT) flags |= PRTERA; if (lflag&ECHOCTL) flags |= CTLECH; if ((iflag&IXANY) == 0) flags |= DECCTQ; flags |= lflag&(ECHO|TOSTOP|FLUSHO|PENDIN|NOFLSH); if (ttydebug) printf("getflags: %x\n", flags); return (flags); } static void ttcompatsetflags(tp, t) register struct tty *tp; register struct termios *t; { register int flags = tp->t_flags; register tcflag_t iflag = t->c_iflag; register tcflag_t oflag = t->c_oflag; register tcflag_t lflag = t->c_lflag; register tcflag_t cflag = t->c_cflag; if (flags & RAW) { iflag = IGNBRK; lflag &= ~(ECHOCTL|ISIG|ICANON|IEXTEN); } else { iflag &= ~(PARMRK|IGNPAR|IGNCR|INLCR); iflag |= BRKINT|IXON|IMAXBEL; lflag |= ISIG|IEXTEN|ECHOCTL; /* XXX was echoctl on ? */ if (flags & XTABS) oflag |= OXTABS; else oflag &= ~OXTABS; if (flags & CBREAK) lflag &= ~ICANON; else lflag |= ICANON; if (flags&CRMOD) { iflag |= ICRNL; oflag |= ONLCR; } else { iflag &= ~ICRNL; oflag &= ~ONLCR; } } if (flags&ECHO) lflag |= ECHO; else lflag &= ~ECHO; cflag &= ~(CSIZE|PARENB); if (flags&(RAW|LITOUT|PASS8)) { cflag |= CS8; if (!(flags&(RAW|PASS8)) || (flags&(RAW|PASS8|ANYP)) == (PASS8|ANYP)) iflag |= ISTRIP; else iflag &= ~ISTRIP; if (flags&(RAW|LITOUT)) oflag &= ~OPOST; else oflag |= OPOST; } else { cflag |= CS7|PARENB; iflag |= ISTRIP; oflag |= OPOST; } /* XXX don't set INPCK if RAW or PASS8? */ if ((flags&(EVENP|ODDP)) == EVENP) { iflag |= INPCK; cflag &= ~PARODD; } else if ((flags&(EVENP|ODDP)) == ODDP) { iflag |= INPCK; cflag |= PARODD; } else iflag &= ~INPCK; if (flags&TANDEM) iflag |= IXOFF; else iflag &= ~IXOFF; if ((flags&DECCTQ) == 0) iflag |= IXANY; else iflag &= ~IXANY; t->c_iflag = iflag; t->c_oflag = oflag; t->c_lflag = lflag; t->c_cflag = cflag; } static void ttcompatsetlflags(tp, t) register struct tty *tp; register struct termios *t; { register int flags = tp->t_flags; register tcflag_t iflag = t->c_iflag; register tcflag_t oflag = t->c_oflag; register tcflag_t lflag = t->c_lflag; register tcflag_t cflag = t->c_cflag; iflag &= ~(PARMRK|IGNPAR|IGNCR|INLCR); if (flags&CRTERA) lflag |= ECHOE; else lflag &= ~ECHOE; if (flags&CRTKIL) lflag |= ECHOKE; else lflag &= ~ECHOKE; if (flags&PRTERA) lflag |= ECHOPRT; else lflag &= ~ECHOPRT; if (flags&CTLECH) lflag |= ECHOCTL; else lflag &= ~ECHOCTL; if (flags&TANDEM) iflag |= IXOFF; else iflag &= ~IXOFF; if ((flags&DECCTQ) == 0) iflag |= IXANY; else iflag &= ~IXANY; if (flags & MDMBUF) cflag |= MDMBUF; else cflag &= ~MDMBUF; if (flags&NOHANG) cflag &= ~HUPCL; else cflag |= HUPCL; lflag &= ~(TOSTOP|FLUSHO|PENDIN|NOFLSH); lflag |= flags&(TOSTOP|FLUSHO|PENDIN|NOFLSH); /* * The next if-else statement is copied from above so don't bother * checking it separately. We could avoid fiddlling with the * character size if the mode is already RAW or if neither the * LITOUT bit or the PASS8 bit is being changed, but the delta of * the change is not available here and skipping the RAW case would * make the code different from above. */ cflag &= ~(CSIZE|PARENB); if (flags&(RAW|LITOUT|PASS8)) { cflag |= CS8; if (!(flags&(RAW|PASS8)) || (flags&(RAW|PASS8|ANYP)) == (PASS8|ANYP)) iflag |= ISTRIP; else iflag &= ~ISTRIP; if (flags&(RAW|LITOUT)) oflag &= ~OPOST; else oflag |= OPOST; } else { cflag |= CS7|PARENB; iflag |= ISTRIP; oflag |= OPOST; } t->c_iflag = iflag; t->c_oflag = oflag; t->c_lflag = lflag; t->c_cflag = cflag; } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ Index: head/sys/kern/tty_pty.c =================================================================== --- head/sys/kern/tty_pty.c (revision 130343) +++ head/sys/kern/tty_pty.c (revision 130344) @@ -1,816 +1,816 @@ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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. * 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. * * @(#)tty_pty.c 8.4 (Berkeley) 2/20/95 */ #include __FBSDID("$FreeBSD$"); /* * Pseudo-teletype Driver * (Actually two drivers, requiring two entries in 'cdevsw') */ #include "opt_compat.h" #include "opt_tty.h" #include #include #include #include #include -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #include #endif #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PTY, "ptys", "pty data structures"); static void ptsstart(struct tty *tp); static void ptsstop(struct tty *tp, int rw); static void ptcwakeup(struct tty *tp, int flag); static dev_t ptyinit(dev_t cdev); static d_open_t ptsopen; static d_close_t ptsclose; static d_read_t ptsread; static d_write_t ptswrite; static d_ioctl_t ptyioctl; static d_open_t ptcopen; static d_close_t ptcclose; static d_read_t ptcread; static d_write_t ptcwrite; static d_poll_t ptcpoll; #define CDEV_MAJOR_S 5 static struct cdevsw pts_cdevsw = { .d_version = D_VERSION, .d_open = ptsopen, .d_close = ptsclose, .d_read = ptsread, .d_write = ptswrite, .d_ioctl = ptyioctl, .d_name = "pts", .d_maj = CDEV_MAJOR_S, .d_flags = D_TTY | D_NEEDGIANT, }; #define CDEV_MAJOR_C 6 static struct cdevsw ptc_cdevsw = { .d_version = D_VERSION, .d_open = ptcopen, .d_close = ptcclose, .d_read = ptcread, .d_write = ptcwrite, .d_ioctl = ptyioctl, .d_poll = ptcpoll, .d_name = "ptc", .d_maj = CDEV_MAJOR_C, .d_flags = D_TTY | D_NEEDGIANT, }; #define BUFSIZ 100 /* Chunk size iomoved to/from user */ struct ptsc { int pt_flags; struct selinfo pt_selr, pt_selw; u_char pt_send; u_char pt_ucntl; struct tty *pt_tty; dev_t devs, devc; struct prison *pt_prison; }; #define PF_PKT 0x08 /* packet mode */ #define PF_STOPPED 0x10 /* user told stopped */ #define PF_REMOTE 0x20 /* remote and flow controlled input */ #define PF_NOSTOP 0x40 #define PF_UCNTL 0x80 /* user control mode */ #define TSA_PTC_READ(tp) ((void *)&(tp)->t_outq.c_cf) #define TSA_PTC_WRITE(tp) ((void *)&(tp)->t_rawq.c_cl) #define TSA_PTS_READ(tp) ((void *)&(tp)->t_canq) static char *names = "pqrsPQRS"; /* * This function creates and initializes a pts/ptc pair * * pts == /dev/tty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv] * ptc == /dev/pty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv] * * XXX: define and add mapping of upper minor bits to allow more * than 256 ptys. */ static dev_t ptyinit(dev_t devc) { dev_t devs; struct ptsc *pt; int n; n = minor(devc); /* For now we only map the lower 8 bits of the minor */ if (n & ~0xff) return (NODEV); devc->si_flags &= ~SI_CHEAPCLONE; pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO); pt->devs = devs = make_dev(&pts_cdevsw, n, UID_ROOT, GID_WHEEL, 0666, "tty%c%r", names[n / 32], n % 32); pt->devc = devc; pt->pt_tty = ttymalloc(pt->pt_tty); devs->si_drv1 = devc->si_drv1 = pt; devs->si_tty = devc->si_tty = pt->pt_tty; pt->pt_tty->t_dev = devs; return (devc); } /*ARGSUSED*/ static int ptsopen(dev_t dev, int flag, int devtype, struct thread *td) { struct tty *tp; int error; struct ptsc *pt; if (!dev->si_drv1) return(ENXIO); pt = dev->si_drv1; tp = dev->si_tty; if ((tp->t_state & TS_ISOPEN) == 0) { ttychars(tp); /* Set up default chars */ tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; } else if (tp->t_state & TS_XCLUDE && suser(td)) return (EBUSY); else if (pt->pt_prison != td->td_ucred->cr_prison) return (EBUSY); if (tp->t_oproc) /* Ctrlr still around. */ (void)ttyld_modem(tp, 1); while ((tp->t_state & TS_CARR_ON) == 0) { if (flag&FNONBLOCK) break; error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, "ptsopn", 0); if (error) return (error); } error = ttyld_open(tp, dev); if (error == 0) ptcwakeup(tp, FREAD|FWRITE); return (error); } static int ptsclose(dev_t dev, int flag, int mode, struct thread *td) { struct tty *tp; int err; tp = dev->si_tty; err = ttyld_close(tp, flag); (void) ttyclose(tp); return (err); } static int ptsread(dev_t dev, struct uio *uio, int flag) { struct thread *td = curthread; struct proc *p = td->td_proc; struct tty *tp = dev->si_tty; struct ptsc *pt = dev->si_drv1; struct pgrp *pg; int error = 0; again: if (pt->pt_flags & PF_REMOTE) { while (isbackground(p, tp)) { sx_slock(&proctree_lock); PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTIN) || SIGISMEMBER(td->td_sigmask, SIGTTIN) || p->p_pgrp->pg_jobc == 0 || p->p_flag & P_PPWAIT) { PROC_UNLOCK(p); sx_sunlock(&proctree_lock); return (EIO); } pg = p->p_pgrp; PROC_UNLOCK(p); PGRP_LOCK(pg); sx_sunlock(&proctree_lock); pgsignal(pg, SIGTTIN, 1); PGRP_UNLOCK(pg); error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ptsbg", 0); if (error) return (error); } if (tp->t_canq.c_cc == 0) { if (flag & IO_NDELAY) return (EWOULDBLOCK); error = ttysleep(tp, TSA_PTS_READ(tp), TTIPRI | PCATCH, "ptsin", 0); if (error) return (error); goto again; } while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0) if (ureadc(getc(&tp->t_canq), uio) < 0) { error = EFAULT; break; } if (tp->t_canq.c_cc == 1) (void) getc(&tp->t_canq); if (tp->t_canq.c_cc) return (error); } else if (tp->t_oproc) error = ttyld_read(tp, uio, flag); ptcwakeup(tp, FWRITE); return (error); } /* * Write to pseudo-tty. * Wakeups of controlling tty will happen * indirectly, when tty driver calls ptsstart. */ static int ptswrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp; tp = dev->si_tty; if (tp->t_oproc == 0) return (EIO); return (ttyld_write(tp, uio, flag)); } /* * Start output on pseudo-tty. * Wake up process selecting or sleeping for input from controlling tty. */ static void ptsstart(struct tty *tp) { struct ptsc *pt = tp->t_dev->si_drv1; if (tp->t_state & TS_TTSTOP) return; if (pt->pt_flags & PF_STOPPED) { pt->pt_flags &= ~PF_STOPPED; pt->pt_send = TIOCPKT_START; } ptcwakeup(tp, FREAD); } static void ptcwakeup(struct tty *tp, int flag) { struct ptsc *pt = tp->t_dev->si_drv1; if (flag & FREAD) { selwakeuppri(&pt->pt_selr, TTIPRI); wakeup(TSA_PTC_READ(tp)); } if (flag & FWRITE) { selwakeuppri(&pt->pt_selw, TTOPRI); wakeup(TSA_PTC_WRITE(tp)); } } static int ptcopen(dev_t dev, int flag, int devtype, struct thread *td) { struct tty *tp; struct ptsc *pt; if (!dev->si_drv1) ptyinit(dev); if (!dev->si_drv1) return(ENXIO); tp = dev->si_tty; if (tp->t_oproc) return (EIO); tp->t_timeout = -1; tp->t_oproc = ptsstart; tp->t_stop = ptsstop; (void)ttyld_modem(tp, 1); tp->t_lflag &= ~EXTPROC; pt = dev->si_drv1; pt->pt_prison = td->td_ucred->cr_prison; pt->pt_flags = 0; pt->pt_send = 0; pt->pt_ucntl = 0; return (0); } static int ptcclose(dev_t dev, int flags, int fmt, struct thread *td) { struct tty *tp; tp = dev->si_tty; (void)ttyld_modem(tp, 0); /* * XXX MDMBUF makes no sense for ptys but would inhibit the above * l_modem(). CLOCAL makes sense but isn't supported. Special * l_modem()s that ignore carrier drop make no sense for ptys but * may be in use because other parts of the line discipline make * sense for ptys. Recover by doing everything that a normal * ttymodem() would have done except for sending a SIGHUP. */ if (tp->t_state & TS_ISOPEN) { tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED); tp->t_state |= TS_ZOMBIE; ttyflush(tp, FREAD | FWRITE); } tp->t_oproc = 0; /* mark closed */ return (0); } static int ptcread(dev_t dev, struct uio *uio, int flag) { struct tty *tp = dev->si_tty; struct ptsc *pt = dev->si_drv1; char buf[BUFSIZ]; int error = 0, cc; /* * We want to block until the slave * is open, and there's something to read; * but if we lost the slave or we're NBIO, * then return the appropriate error instead. */ for (;;) { if (tp->t_state&TS_ISOPEN) { if (pt->pt_flags&PF_PKT && pt->pt_send) { error = ureadc((int)pt->pt_send, uio); if (error) return (error); if (pt->pt_send & TIOCPKT_IOCTL) { cc = min(uio->uio_resid, sizeof(tp->t_termios)); uiomove(&tp->t_termios, cc, uio); } pt->pt_send = 0; return (0); } if (pt->pt_flags&PF_UCNTL && pt->pt_ucntl) { error = ureadc((int)pt->pt_ucntl, uio); if (error) return (error); pt->pt_ucntl = 0; return (0); } if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) break; } if ((tp->t_state & TS_CONNECTED) == 0) return (0); /* EOF */ if (flag & IO_NDELAY) return (EWOULDBLOCK); error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH, "ptcin", 0); if (error) return (error); } if (pt->pt_flags & (PF_PKT|PF_UCNTL)) error = ureadc(0, uio); while (uio->uio_resid > 0 && error == 0) { cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ)); if (cc <= 0) break; error = uiomove(buf, cc, uio); } ttwwakeup(tp); return (error); } static void ptsstop(struct tty *tp, int flush) { struct ptsc *pt = tp->t_dev->si_drv1; int flag; /* note: FLUSHREAD and FLUSHWRITE already ok */ if (flush == 0) { flush = TIOCPKT_STOP; pt->pt_flags |= PF_STOPPED; } else pt->pt_flags &= ~PF_STOPPED; pt->pt_send |= flush; /* change of perspective */ flag = 0; if (flush & FREAD) flag |= FWRITE; if (flush & FWRITE) flag |= FREAD; ptcwakeup(tp, flag); } static int ptcpoll(dev_t dev, int events, struct thread *td) { struct tty *tp = dev->si_tty; struct ptsc *pt = dev->si_drv1; int revents = 0; int s; if ((tp->t_state & TS_CONNECTED) == 0) return (events & (POLLHUP | POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)); /* * Need to block timeouts (ttrstart). */ s = spltty(); if (events & (POLLIN | POLLRDNORM)) if ((tp->t_state & TS_ISOPEN) && ((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) || ((pt->pt_flags & PF_PKT) && pt->pt_send) || ((pt->pt_flags & PF_UCNTL) && pt->pt_ucntl))) revents |= events & (POLLIN | POLLRDNORM); if (events & (POLLOUT | POLLWRNORM)) if (tp->t_state & TS_ISOPEN && ((pt->pt_flags & PF_REMOTE) ? (tp->t_canq.c_cc == 0) : ((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) || (tp->t_canq.c_cc == 0 && (tp->t_lflag & ICANON))))) revents |= events & (POLLOUT | POLLWRNORM); if (events & POLLHUP) if ((tp->t_state & TS_CARR_ON) == 0) revents |= POLLHUP; if (revents == 0) { if (events & (POLLIN | POLLRDNORM)) selrecord(td, &pt->pt_selr); if (events & (POLLOUT | POLLWRNORM)) selrecord(td, &pt->pt_selw); } splx(s); return (revents); } static int ptcwrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp = dev->si_tty; u_char *cp = 0; int cc = 0; u_char locbuf[BUFSIZ]; int cnt = 0; struct ptsc *pt = dev->si_drv1; int error = 0; again: if ((tp->t_state&TS_ISOPEN) == 0) goto block; if (pt->pt_flags & PF_REMOTE) { if (tp->t_canq.c_cc) goto block; while ((uio->uio_resid > 0 || cc > 0) && tp->t_canq.c_cc < TTYHOG - 1) { if (cc == 0) { cc = min(uio->uio_resid, BUFSIZ); cc = min(cc, TTYHOG - 1 - tp->t_canq.c_cc); cp = locbuf; error = uiomove(cp, cc, uio); if (error) return (error); /* check again for safety */ if ((tp->t_state & TS_ISOPEN) == 0) { /* adjust as usual */ uio->uio_resid += cc; return (EIO); } } if (cc > 0) { cc = b_to_q((char *)cp, cc, &tp->t_canq); /* * XXX we don't guarantee that the canq size * is >= TTYHOG, so the above b_to_q() may * leave some bytes uncopied. However, space * is guaranteed for the null terminator if * we don't fail here since (TTYHOG - 1) is * not a multiple of CBSIZE. */ if (cc > 0) break; } } /* adjust for data copied in but not written */ uio->uio_resid += cc; (void) putc(0, &tp->t_canq); ttwakeup(tp); wakeup(TSA_PTS_READ(tp)); return (0); } while (uio->uio_resid > 0 || cc > 0) { if (cc == 0) { cc = min(uio->uio_resid, BUFSIZ); cp = locbuf; error = uiomove(cp, cc, uio); if (error) return (error); /* check again for safety */ if ((tp->t_state & TS_ISOPEN) == 0) { /* adjust for data copied in but not written */ uio->uio_resid += cc; return (EIO); } } while (cc > 0) { if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 && (tp->t_canq.c_cc > 0 || !(tp->t_lflag&ICANON))) { wakeup(TSA_HUP_OR_INPUT(tp)); goto block; } ttyld_rint(tp, *cp++); cnt++; cc--; } cc = 0; } return (0); block: /* * Come here to wait for slave to open, for space * in outq, or space in rawq, or an empty canq. */ if ((tp->t_state & TS_CONNECTED) == 0) { /* adjust for data copied in but not written */ uio->uio_resid += cc; return (EIO); } if (flag & IO_NDELAY) { /* adjust for data copied in but not written */ uio->uio_resid += cc; if (cnt == 0) return (EWOULDBLOCK); return (0); } error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH, "ptcout", 0); if (error) { /* adjust for data copied in but not written */ uio->uio_resid += cc; return (error); } goto again; } /*ARGSUSED*/ static int ptyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct tty *tp = dev->si_tty; struct ptsc *pt = dev->si_drv1; u_char *cc = tp->t_cc; int stop, error; if (devsw(dev)->d_open == ptcopen) { switch (cmd) { case TIOCGPGRP: /* * We avoid calling ttioctl on the controller since, * in that case, tp must be the controlling terminal. */ *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; return (0); case TIOCPKT: if (*(int *)data) { if (pt->pt_flags & PF_UCNTL) return (EINVAL); pt->pt_flags |= PF_PKT; } else pt->pt_flags &= ~PF_PKT; return (0); case TIOCUCNTL: if (*(int *)data) { if (pt->pt_flags & PF_PKT) return (EINVAL); pt->pt_flags |= PF_UCNTL; } else pt->pt_flags &= ~PF_UCNTL; return (0); case TIOCREMOTE: if (*(int *)data) pt->pt_flags |= PF_REMOTE; else pt->pt_flags &= ~PF_REMOTE; ttyflush(tp, FREAD|FWRITE); return (0); } /* * The rest of the ioctls shouldn't be called until * the slave is open. */ if ((tp->t_state & TS_ISOPEN) == 0) return (EAGAIN); switch (cmd) { #ifdef COMPAT_43 case TIOCSETP: case TIOCSETN: #endif case TIOCSETD: case TIOCSETA: case TIOCSETAW: case TIOCSETAF: /* * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG. * ttywflush(tp) will hang if there are characters in * the outq. */ ndflush(&tp->t_outq, tp->t_outq.c_cc); break; case TIOCSIG: if (*(unsigned int *)data >= NSIG || *(unsigned int *)data == 0) return(EINVAL); if ((tp->t_lflag&NOFLSH) == 0) ttyflush(tp, FREAD|FWRITE); if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, *(unsigned int *)data, 1); PGRP_UNLOCK(tp->t_pgrp); } if ((*(unsigned int *)data == SIGINFO) && ((tp->t_lflag&NOKERNINFO) == 0)) ttyinfo(tp); return(0); } } if (cmd == TIOCEXT) { /* * When the EXTPROC bit is being toggled, we need * to send an TIOCPKT_IOCTL if the packet driver * is turned on. */ if (*(int *)data) { if (pt->pt_flags & PF_PKT) { pt->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); } tp->t_lflag |= EXTPROC; } else { if ((tp->t_lflag & EXTPROC) && (pt->pt_flags & PF_PKT)) { pt->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); } tp->t_lflag &= ~EXTPROC; } return(0); } error = ttyioctl(dev, cmd, data, flag, td); if (error == ENOTTY) { if (pt->pt_flags & PF_UCNTL && (cmd & ~0xff) == UIOCCMD(0)) { if (cmd & 0xff) { pt->pt_ucntl = (u_char)cmd; ptcwakeup(tp, FREAD); } return (0); } error = ENOTTY; } /* * If external processing and packet mode send ioctl packet. */ if ((tp->t_lflag&EXTPROC) && (pt->pt_flags & PF_PKT)) { switch(cmd) { case TIOCSETA: case TIOCSETAW: case TIOCSETAF: #ifdef COMPAT_43 case TIOCSETP: case TIOCSETN: #endif -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) case TIOCSETC: case TIOCSLTC: case TIOCLBIS: case TIOCLBIC: case TIOCLSET: #endif pt->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); break; default: break; } } stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) && CCEQ(cc[VSTART], CTRL('q')); if (pt->pt_flags & PF_NOSTOP) { if (stop) { pt->pt_send &= ~TIOCPKT_NOSTOP; pt->pt_send |= TIOCPKT_DOSTOP; pt->pt_flags &= ~PF_NOSTOP; ptcwakeup(tp, FREAD); } } else { if (!stop) { pt->pt_send &= ~TIOCPKT_DOSTOP; pt->pt_send |= TIOCPKT_NOSTOP; pt->pt_flags |= PF_NOSTOP; ptcwakeup(tp, FREAD); } } return (error); } static void pty_clone(void *arg, char *name, int namelen, dev_t *dev) { int u; if (*dev != NODEV) return; if (bcmp(name, "pty", 3) != 0) return; if (name[5] != '\0') return; switch (name[3]) { case 'p': u = 0; break; case 'q': u = 32; break; case 'r': u = 64; break; case 's': u = 96; break; case 'P': u = 128; break; case 'Q': u = 160; break; case 'R': u = 192; break; case 'S': u = 224; break; default: return; } if (name[4] >= '0' && name[4] <= '9') u += name[4] - '0'; else if (name[4] >= 'a' && name[4] <= 'v') u += name[4] - 'a' + 10; else return; *dev = make_dev(&ptc_cdevsw, u, UID_ROOT, GID_WHEEL, 0666, "pty%c%r", names[u / 32], u % 32); (*dev)->si_flags |= SI_CHEAPCLONE; return; } static void ptc_drvinit(void *unused) { EVENTHANDLER_REGISTER(dev_clone, pty_clone, 0, 1000); } SYSINIT(ptcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR_C,ptc_drvinit,NULL) Index: head/sys/kern/uipc_syscalls.c =================================================================== --- head/sys/kern/uipc_syscalls.c (revision 130343) +++ head/sys/kern/uipc_syscalls.c (revision 130344) @@ -1,2085 +1,2085 @@ /* * Copyright (c) 1982, 1986, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * sendfile(2) and related extensions: * Copyright (c) 1998, David Greenman. All rights reserved. * * 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. * 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. * * @(#)uipc_syscalls.c 8.4 (Berkeley) 2/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_ktrace.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KTRACE #include #endif #include #include #include #include #include #include static int sendit(struct thread *td, int s, struct msghdr *mp, int flags); static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp); static int accept1(struct thread *td, struct accept_args *uap, int compat); static int do_sendfile(struct thread *td, struct sendfile_args *uap, int compat); static int getsockname1(struct thread *td, struct getsockname_args *uap, int compat); static int getpeername1(struct thread *td, struct getpeername_args *uap, int compat); /* * NSFBUFS-related variables and associated sysctls */ int nsfbufs; int nsfbufspeak; int nsfbufsused; SYSCTL_DECL(_kern_ipc); SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufs, CTLFLAG_RDTUN, &nsfbufs, 0, "Maximum number of sendfile(2) sf_bufs available"); SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufspeak, CTLFLAG_RD, &nsfbufspeak, 0, "Number of sendfile(2) sf_bufs at peak usage"); SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufsused, CTLFLAG_RD, &nsfbufsused, 0, "Number of sendfile(2) sf_bufs in use"); /* * System call interface to the socket abstraction. */ -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #define COMPAT_OLDSOCK #endif /* * MPSAFE */ int socket(td, uap) struct thread *td; register struct socket_args /* { int domain; int type; int protocol; } */ *uap; { struct filedesc *fdp; struct socket *so; struct file *fp; int fd, error; fdp = td->td_proc->p_fd; error = falloc(td, &fp, &fd); if (error) return (error); /* An extra reference on `fp' has been held for us by falloc(). */ NET_LOCK_GIANT(); error = socreate(uap->domain, &so, uap->type, uap->protocol, td->td_ucred, td); NET_UNLOCK_GIANT(); FILEDESC_LOCK(fdp); if (error) { if (fdp->fd_ofiles[fd] == fp) { fdp->fd_ofiles[fd] = NULL; fdunused(fdp, fd); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else { FILEDESC_UNLOCK(fdp); } } else { fp->f_data = so; /* already has ref count */ fp->f_flag = FREAD|FWRITE; fp->f_ops = &socketops; fp->f_type = DTYPE_SOCKET; FILEDESC_UNLOCK(fdp); td->td_retval[0] = fd; } fdrop(fp, td); return (error); } /* * MPSAFE */ /* ARGSUSED */ int bind(td, uap) struct thread *td; register struct bind_args /* { int s; caddr_t name; int namelen; } */ *uap; { struct sockaddr *sa; int error; if ((error = getsockaddr(&sa, uap->name, uap->namelen)) != 0) return (error); return (kern_bind(td, uap->s, sa)); } int kern_bind(td, fd, sa) struct thread *td; int fd; struct sockaddr *sa; { struct socket *so; int error; NET_LOCK_GIANT(); if ((error = fgetsock(td, fd, &so, NULL)) != 0) goto done2; #ifdef MAC error = mac_check_socket_bind(td->td_ucred, so, sa); if (error) goto done1; #endif error = sobind(so, sa, td); #ifdef MAC done1: #endif fputsock(so); done2: NET_UNLOCK_GIANT(); FREE(sa, M_SONAME); return (error); } /* * MPSAFE */ /* ARGSUSED */ int listen(td, uap) struct thread *td; register struct listen_args /* { int s; int backlog; } */ *uap; { struct socket *so; int error; NET_LOCK_GIANT(); if ((error = fgetsock(td, uap->s, &so, NULL)) == 0) { #ifdef MAC error = mac_check_socket_listen(td->td_ucred, so); if (error) goto done; #endif error = solisten(so, uap->backlog, td); #ifdef MAC done: #endif fputsock(so); } NET_UNLOCK_GIANT(); return(error); } /* * accept1() * MPSAFE */ static int accept1(td, uap, compat) struct thread *td; register struct accept_args /* { int s; struct sockaddr * __restrict name; socklen_t * __restrict anamelen; } */ *uap; int compat; { struct filedesc *fdp; struct file *nfp = NULL; struct sockaddr *sa = NULL; socklen_t namelen; int error; struct socket *head, *so; int fd; u_int fflag; pid_t pgid; int tmp; fdp = td->td_proc->p_fd; if (uap->name) { error = copyin(uap->anamelen, &namelen, sizeof (namelen)); if(error) return (error); if (namelen < 0) return (EINVAL); } NET_LOCK_GIANT(); error = fgetsock(td, uap->s, &head, &fflag); if (error) goto done2; if ((head->so_options & SO_ACCEPTCONN) == 0) { error = EINVAL; goto done; } error = falloc(td, &nfp, &fd); if (error) goto done; ACCEPT_LOCK(); if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->so_comp)) { ACCEPT_UNLOCK(); error = EWOULDBLOCK; goto noconnection; } while (TAILQ_EMPTY(&head->so_comp) && head->so_error == 0) { if (head->so_state & SS_CANTRCVMORE) { head->so_error = ECONNABORTED; break; } error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH, "accept", 0); if (error) { ACCEPT_UNLOCK(); goto noconnection; } } if (head->so_error) { error = head->so_error; head->so_error = 0; ACCEPT_UNLOCK(); goto noconnection; } so = TAILQ_FIRST(&head->so_comp); KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP")); KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP")); soref(so); /* file descriptor reference */ TAILQ_REMOVE(&head->so_comp, so, so_list); head->so_qlen--; so->so_qstate &= ~SQ_COMP; so->so_head = NULL; ACCEPT_UNLOCK(); /* An extra reference on `nfp' has been held for us by falloc(). */ td->td_retval[0] = fd; /* connection has been removed from the listen queue */ KNOTE(&head->so_rcv.sb_sel.si_note, 0); pgid = fgetown(&head->so_sigio); if (pgid != 0) fsetown(pgid, &so->so_sigio); FILE_LOCK(nfp); nfp->f_data = so; /* nfp has ref count from falloc */ nfp->f_flag = fflag; nfp->f_ops = &socketops; nfp->f_type = DTYPE_SOCKET; FILE_UNLOCK(nfp); /* Sync socket nonblocking/async state with file flags */ tmp = fflag & FNONBLOCK; (void) fo_ioctl(nfp, FIONBIO, &tmp, td->td_ucred, td); tmp = fflag & FASYNC; (void) fo_ioctl(nfp, FIOASYNC, &tmp, td->td_ucred, td); sa = 0; error = soaccept(so, &sa); if (error) { /* * return a namelen of zero for older code which might * ignore the return value from accept. */ if (uap->name != NULL) { namelen = 0; (void) copyout(&namelen, uap->anamelen, sizeof(*uap->anamelen)); } goto noconnection; } if (sa == NULL) { namelen = 0; if (uap->name) goto gotnoname; error = 0; goto done; } if (uap->name) { /* check sa_len before it is destroyed */ if (namelen > sa->sa_len) namelen = sa->sa_len; #ifdef COMPAT_OLDSOCK if (compat) ((struct osockaddr *)sa)->sa_family = sa->sa_family; #endif error = copyout(sa, uap->name, (u_int)namelen); if (!error) gotnoname: error = copyout(&namelen, uap->anamelen, sizeof (*uap->anamelen)); } noconnection: if (sa) FREE(sa, M_SONAME); /* * close the new descriptor, assuming someone hasn't ripped it * out from under us. */ if (error) { FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[fd] == nfp) { fdp->fd_ofiles[fd] = NULL; fdunused(fdp, fd); FILEDESC_UNLOCK(fdp); fdrop(nfp, td); } else { FILEDESC_UNLOCK(fdp); } } /* * Release explicitly held references before returning. */ done: if (nfp != NULL) fdrop(nfp, td); fputsock(head); done2: NET_UNLOCK_GIANT(); return (error); } /* * MPSAFE (accept1() is MPSAFE) */ int accept(td, uap) struct thread *td; struct accept_args *uap; { return (accept1(td, uap, 0)); } #ifdef COMPAT_OLDSOCK /* * MPSAFE (accept1() is MPSAFE) */ int oaccept(td, uap) struct thread *td; struct accept_args *uap; { return (accept1(td, uap, 1)); } #endif /* COMPAT_OLDSOCK */ /* * MPSAFE */ /* ARGSUSED */ int connect(td, uap) struct thread *td; register struct connect_args /* { int s; caddr_t name; int namelen; } */ *uap; { struct sockaddr *sa; int error; error = getsockaddr(&sa, uap->name, uap->namelen); if (error) return (error); return (kern_connect(td, uap->s, sa)); } int kern_connect(td, fd, sa) struct thread *td; int fd; struct sockaddr *sa; { struct socket *so; int error, s; int interrupted = 0; NET_LOCK_GIANT(); if ((error = fgetsock(td, fd, &so, NULL)) != 0) goto done2; if (so->so_state & SS_ISCONNECTING) { error = EALREADY; goto done1; } #ifdef MAC error = mac_check_socket_connect(td->td_ucred, so, sa); if (error) goto bad; #endif error = soconnect(so, sa, td); if (error) goto bad; if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { error = EINPROGRESS; goto done1; } s = splnet(); while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { error = tsleep(&so->so_timeo, PSOCK | PCATCH, "connec", 0); if (error) { if (error == EINTR || error == ERESTART) interrupted = 1; break; } } if (error == 0) { error = so->so_error; so->so_error = 0; } splx(s); bad: if (!interrupted) so->so_state &= ~SS_ISCONNECTING; if (error == ERESTART) error = EINTR; done1: fputsock(so); done2: NET_UNLOCK_GIANT(); FREE(sa, M_SONAME); return (error); } /* * MPSAFE */ int socketpair(td, uap) struct thread *td; register struct socketpair_args /* { int domain; int type; int protocol; int *rsv; } */ *uap; { register struct filedesc *fdp = td->td_proc->p_fd; struct file *fp1, *fp2; struct socket *so1, *so2; int fd, error, sv[2]; NET_LOCK_GIANT(); error = socreate(uap->domain, &so1, uap->type, uap->protocol, td->td_ucred, td); if (error) goto done2; error = socreate(uap->domain, &so2, uap->type, uap->protocol, td->td_ucred, td); if (error) goto free1; /* On success extra reference to `fp1' and 'fp2' is set by falloc. */ error = falloc(td, &fp1, &fd); if (error) goto free2; sv[0] = fd; fp1->f_data = so1; /* so1 already has ref count */ error = falloc(td, &fp2, &fd); if (error) goto free3; fp2->f_data = so2; /* so2 already has ref count */ sv[1] = fd; error = soconnect2(so1, so2); if (error) goto free4; if (uap->type == SOCK_DGRAM) { /* * Datagram socket connection is asymmetric. */ error = soconnect2(so2, so1); if (error) goto free4; } FILE_LOCK(fp1); fp1->f_flag = FREAD|FWRITE; fp1->f_ops = &socketops; fp1->f_type = DTYPE_SOCKET; FILE_UNLOCK(fp1); FILE_LOCK(fp2); fp2->f_flag = FREAD|FWRITE; fp2->f_ops = &socketops; fp2->f_type = DTYPE_SOCKET; FILE_UNLOCK(fp2); error = copyout(sv, uap->rsv, 2 * sizeof (int)); fdrop(fp1, td); fdrop(fp2, td); goto done2; free4: FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[sv[1]] == fp2) { fdp->fd_ofiles[sv[1]] = NULL; fdunused(fdp, sv[1]); FILEDESC_UNLOCK(fdp); fdrop(fp2, td); } else { FILEDESC_UNLOCK(fdp); } fdrop(fp2, td); free3: FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[sv[0]] == fp1) { fdp->fd_ofiles[sv[0]] = NULL; fdunused(fdp, sv[0]); FILEDESC_UNLOCK(fdp); fdrop(fp1, td); } else { FILEDESC_UNLOCK(fdp); } fdrop(fp1, td); free2: (void)soclose(so2); free1: (void)soclose(so1); done2: NET_UNLOCK_GIANT(); return (error); } static int sendit(td, s, mp, flags) register struct thread *td; int s; register struct msghdr *mp; int flags; { struct mbuf *control; struct sockaddr *to; int error; if (mp->msg_name != NULL) { error = getsockaddr(&to, mp->msg_name, mp->msg_namelen); if (error) { to = NULL; goto bad; } mp->msg_name = to; } else { to = NULL; } if (mp->msg_control) { if (mp->msg_controllen < sizeof(struct cmsghdr) #ifdef COMPAT_OLDSOCK && mp->msg_flags != MSG_COMPAT #endif ) { error = EINVAL; goto bad; } error = sockargs(&control, mp->msg_control, mp->msg_controllen, MT_CONTROL); if (error) goto bad; #ifdef COMPAT_OLDSOCK if (mp->msg_flags == MSG_COMPAT) { register struct cmsghdr *cm; M_PREPEND(control, sizeof(*cm), M_TRYWAIT); if (control == 0) { error = ENOBUFS; goto bad; } else { cm = mtod(control, struct cmsghdr *); cm->cmsg_len = control->m_len; cm->cmsg_level = SOL_SOCKET; cm->cmsg_type = SCM_RIGHTS; } } #endif } else { control = NULL; } error = kern_sendit(td, s, mp, flags, control); bad: if (to) FREE(to, M_SONAME); return (error); } int kern_sendit(td, s, mp, flags, control) struct thread *td; int s; struct msghdr *mp; int flags; struct mbuf *control; { struct uio auio; struct iovec *iov; struct socket *so; int i; int len, error; #ifdef KTRACE struct iovec *ktriov = NULL; struct uio ktruio; int iovlen; #endif NET_LOCK_GIANT(); if ((error = fgetsock(td, s, &so, NULL)) != 0) goto bad2; #ifdef MAC error = mac_check_socket_send(td->td_ucred, so); if (error) goto bad; #endif auio.uio_iov = mp->msg_iov; auio.uio_iovcnt = mp->msg_iovlen; auio.uio_segflg = UIO_USERSPACE; auio.uio_rw = UIO_WRITE; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; iov = mp->msg_iov; for (i = 0; i < mp->msg_iovlen; i++, iov++) { if ((auio.uio_resid += iov->iov_len) < 0) { error = EINVAL; goto bad; } } #ifdef KTRACE if (KTRPOINT(td, KTR_GENIO)) { iovlen = auio.uio_iovcnt * sizeof (struct iovec); MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK); bcopy(auio.uio_iov, ktriov, iovlen); ktruio = auio; } #endif len = auio.uio_resid; error = so->so_proto->pr_usrreqs->pru_sosend(so, mp->msg_name, &auio, 0, control, flags, td); if (error) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; /* Generation of SIGPIPE can be controlled per socket */ if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE)) { PROC_LOCK(td->td_proc); psignal(td->td_proc, SIGPIPE); PROC_UNLOCK(td->td_proc); } } if (error == 0) td->td_retval[0] = len - auio.uio_resid; #ifdef KTRACE if (ktriov != NULL) { if (error == 0) { ktruio.uio_iov = ktriov; ktruio.uio_resid = td->td_retval[0]; ktrgenio(s, UIO_WRITE, &ktruio, error); } FREE(ktriov, M_TEMP); } #endif bad: fputsock(so); bad2: NET_UNLOCK_GIANT(); return (error); } /* * MPSAFE */ int sendto(td, uap) struct thread *td; register struct sendto_args /* { int s; caddr_t buf; size_t len; int flags; caddr_t to; int tolen; } */ *uap; { struct msghdr msg; struct iovec aiov; int error; msg.msg_name = uap->to; msg.msg_namelen = uap->tolen; msg.msg_iov = &aiov; msg.msg_iovlen = 1; msg.msg_control = 0; #ifdef COMPAT_OLDSOCK msg.msg_flags = 0; #endif aiov.iov_base = uap->buf; aiov.iov_len = uap->len; error = sendit(td, uap->s, &msg, uap->flags); return (error); } #ifdef COMPAT_OLDSOCK /* * MPSAFE */ int osend(td, uap) struct thread *td; register struct osend_args /* { int s; caddr_t buf; int len; int flags; } */ *uap; { struct msghdr msg; struct iovec aiov; int error; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = 0; error = sendit(td, uap->s, &msg, uap->flags); return (error); } /* * MPSAFE */ int osendmsg(td, uap) struct thread *td; register struct osendmsg_args /* { int s; caddr_t msg; int flags; } */ *uap; { struct msghdr msg; struct iovec aiov[UIO_SMALLIOV], *iov; int error; error = copyin(uap->msg, &msg, sizeof (struct omsghdr)); if (error) goto done2; if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) { if ((u_int)msg.msg_iovlen >= UIO_MAXIOV) { error = EMSGSIZE; goto done2; } MALLOC(iov, struct iovec *, sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV, M_WAITOK); } else { iov = aiov; } error = copyin(msg.msg_iov, iov, (unsigned)(msg.msg_iovlen * sizeof (struct iovec))); if (error) goto done; msg.msg_flags = MSG_COMPAT; msg.msg_iov = iov; error = sendit(td, uap->s, &msg, uap->flags); done: if (iov != aiov) FREE(iov, M_IOV); done2: return (error); } #endif /* * MPSAFE */ int sendmsg(td, uap) struct thread *td; register struct sendmsg_args /* { int s; caddr_t msg; int flags; } */ *uap; { struct msghdr msg; struct iovec aiov[UIO_SMALLIOV], *iov; int error; error = copyin(uap->msg, &msg, sizeof (msg)); if (error) goto done2; if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) { if ((u_int)msg.msg_iovlen >= UIO_MAXIOV) { error = EMSGSIZE; goto done2; } MALLOC(iov, struct iovec *, sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV, M_WAITOK); } else { iov = aiov; } if (msg.msg_iovlen && (error = copyin(msg.msg_iov, iov, (unsigned)(msg.msg_iovlen * sizeof (struct iovec))))) goto done; msg.msg_iov = iov; #ifdef COMPAT_OLDSOCK msg.msg_flags = 0; #endif error = sendit(td, uap->s, &msg, uap->flags); done: if (iov != aiov) FREE(iov, M_IOV); done2: return (error); } static int recvit(td, s, mp, namelenp) register struct thread *td; int s; register struct msghdr *mp; void *namelenp; { struct uio auio; register struct iovec *iov; register int i; socklen_t len; int error; struct mbuf *m, *control = 0; caddr_t ctlbuf; struct socket *so; struct sockaddr *fromsa = 0; #ifdef KTRACE struct iovec *ktriov = NULL; struct uio ktruio; int iovlen; #endif NET_LOCK_GIANT(); if ((error = fgetsock(td, s, &so, NULL)) != 0) { NET_UNLOCK_GIANT(); return (error); } #ifdef MAC error = mac_check_socket_receive(td->td_ucred, so); if (error) { fputsock(so); NET_UNLOCK_GIANT(); return (error); } #endif auio.uio_iov = mp->msg_iov; auio.uio_iovcnt = mp->msg_iovlen; auio.uio_segflg = UIO_USERSPACE; auio.uio_rw = UIO_READ; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; iov = mp->msg_iov; for (i = 0; i < mp->msg_iovlen; i++, iov++) { if ((auio.uio_resid += iov->iov_len) < 0) { fputsock(so); NET_UNLOCK_GIANT(); return (EINVAL); } } #ifdef KTRACE if (KTRPOINT(td, KTR_GENIO)) { iovlen = auio.uio_iovcnt * sizeof (struct iovec); MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK); bcopy(auio.uio_iov, ktriov, iovlen); ktruio = auio; } #endif len = auio.uio_resid; error = so->so_proto->pr_usrreqs->pru_soreceive(so, &fromsa, &auio, (struct mbuf **)0, mp->msg_control ? &control : (struct mbuf **)0, &mp->msg_flags); if (error) { if (auio.uio_resid != (int)len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; } #ifdef KTRACE if (ktriov != NULL) { if (error == 0) { ktruio.uio_iov = ktriov; ktruio.uio_resid = (int)len - auio.uio_resid; ktrgenio(s, UIO_READ, &ktruio, error); } FREE(ktriov, M_TEMP); } #endif if (error) goto out; td->td_retval[0] = (int)len - auio.uio_resid; if (mp->msg_name) { len = mp->msg_namelen; if (len <= 0 || fromsa == 0) len = 0; else { /* save sa_len before it is destroyed by MSG_COMPAT */ len = MIN(len, fromsa->sa_len); #ifdef COMPAT_OLDSOCK if (mp->msg_flags & MSG_COMPAT) ((struct osockaddr *)fromsa)->sa_family = fromsa->sa_family; #endif error = copyout(fromsa, mp->msg_name, (unsigned)len); if (error) goto out; } mp->msg_namelen = len; if (namelenp && (error = copyout(&len, namelenp, sizeof (socklen_t)))) { #ifdef COMPAT_OLDSOCK if (mp->msg_flags & MSG_COMPAT) error = 0; /* old recvfrom didn't check */ else #endif goto out; } } if (mp->msg_control) { #ifdef COMPAT_OLDSOCK /* * We assume that old recvmsg calls won't receive access * rights and other control info, esp. as control info * is always optional and those options didn't exist in 4.3. * If we receive rights, trim the cmsghdr; anything else * is tossed. */ if (control && mp->msg_flags & MSG_COMPAT) { if (mtod(control, struct cmsghdr *)->cmsg_level != SOL_SOCKET || mtod(control, struct cmsghdr *)->cmsg_type != SCM_RIGHTS) { mp->msg_controllen = 0; goto out; } control->m_len -= sizeof (struct cmsghdr); control->m_data += sizeof (struct cmsghdr); } #endif len = mp->msg_controllen; m = control; mp->msg_controllen = 0; ctlbuf = mp->msg_control; while (m && len > 0) { unsigned int tocopy; if (len >= m->m_len) tocopy = m->m_len; else { mp->msg_flags |= MSG_CTRUNC; tocopy = len; } if ((error = copyout(mtod(m, caddr_t), ctlbuf, tocopy)) != 0) goto out; ctlbuf += tocopy; len -= tocopy; m = m->m_next; } mp->msg_controllen = ctlbuf - (caddr_t)mp->msg_control; } out: fputsock(so); NET_UNLOCK_GIANT(); if (fromsa) FREE(fromsa, M_SONAME); if (control) m_freem(control); return (error); } /* * MPSAFE */ int recvfrom(td, uap) struct thread *td; register struct recvfrom_args /* { int s; caddr_t buf; size_t len; int flags; struct sockaddr * __restrict from; socklen_t * __restrict fromlenaddr; } */ *uap; { struct msghdr msg; struct iovec aiov; int error; if (uap->fromlenaddr) { error = copyin(uap->fromlenaddr, &msg.msg_namelen, sizeof (msg.msg_namelen)); if (error) goto done2; } else { msg.msg_namelen = 0; } msg.msg_name = uap->from; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = uap->flags; error = recvit(td, uap->s, &msg, uap->fromlenaddr); done2: return(error); } #ifdef COMPAT_OLDSOCK /* * MPSAFE */ int orecvfrom(td, uap) struct thread *td; struct recvfrom_args *uap; { uap->flags |= MSG_COMPAT; return (recvfrom(td, uap)); } #endif #ifdef COMPAT_OLDSOCK /* * MPSAFE */ int orecv(td, uap) struct thread *td; register struct orecv_args /* { int s; caddr_t buf; int len; int flags; } */ *uap; { struct msghdr msg; struct iovec aiov; int error; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = uap->flags; error = recvit(td, uap->s, &msg, NULL); return (error); } /* * Old recvmsg. This code takes advantage of the fact that the old msghdr * overlays the new one, missing only the flags, and with the (old) access * rights where the control fields are now. * * MPSAFE */ int orecvmsg(td, uap) struct thread *td; register struct orecvmsg_args /* { int s; struct omsghdr *msg; int flags; } */ *uap; { struct msghdr msg; struct iovec aiov[UIO_SMALLIOV], *iov; int error; error = copyin(uap->msg, &msg, sizeof (struct omsghdr)); if (error) return (error); if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) { if ((u_int)msg.msg_iovlen >= UIO_MAXIOV) { error = EMSGSIZE; goto done2; } MALLOC(iov, struct iovec *, sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV, M_WAITOK); } else { iov = aiov; } msg.msg_flags = uap->flags | MSG_COMPAT; error = copyin(msg.msg_iov, iov, (unsigned)(msg.msg_iovlen * sizeof (struct iovec))); if (error) goto done; msg.msg_iov = iov; error = recvit(td, uap->s, &msg, &uap->msg->msg_namelen); if (msg.msg_controllen && error == 0) error = copyout(&msg.msg_controllen, &uap->msg->msg_accrightslen, sizeof (int)); done: if (iov != aiov) FREE(iov, M_IOV); done2: return (error); } #endif /* * MPSAFE */ int recvmsg(td, uap) struct thread *td; register struct recvmsg_args /* { int s; struct msghdr *msg; int flags; } */ *uap; { struct msghdr msg; struct iovec aiov[UIO_SMALLIOV], *uiov, *iov; register int error; error = copyin(uap->msg, &msg, sizeof (msg)); if (error) goto done2; if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) { if ((u_int)msg.msg_iovlen >= UIO_MAXIOV) { error = EMSGSIZE; goto done2; } MALLOC(iov, struct iovec *, sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV, M_WAITOK); } else { iov = aiov; } #ifdef COMPAT_OLDSOCK msg.msg_flags = uap->flags &~ MSG_COMPAT; #else msg.msg_flags = uap->flags; #endif uiov = msg.msg_iov; msg.msg_iov = iov; error = copyin(uiov, iov, (unsigned)(msg.msg_iovlen * sizeof (struct iovec))); if (error) goto done; error = recvit(td, uap->s, &msg, NULL); if (!error) { msg.msg_iov = uiov; error = copyout(&msg, uap->msg, sizeof(msg)); } done: if (iov != aiov) FREE(iov, M_IOV); done2: return (error); } /* * MPSAFE */ /* ARGSUSED */ int shutdown(td, uap) struct thread *td; register struct shutdown_args /* { int s; int how; } */ *uap; { struct socket *so; int error; NET_LOCK_GIANT(); if ((error = fgetsock(td, uap->s, &so, NULL)) == 0) { error = soshutdown(so, uap->how); fputsock(so); } NET_UNLOCK_GIANT(); return(error); } /* * MPSAFE */ /* ARGSUSED */ int setsockopt(td, uap) struct thread *td; register struct setsockopt_args /* { int s; int level; int name; caddr_t val; int valsize; } */ *uap; { struct socket *so; struct sockopt sopt; int error; if (uap->val == 0 && uap->valsize != 0) return (EFAULT); if (uap->valsize < 0) return (EINVAL); NET_LOCK_GIANT(); if ((error = fgetsock(td, uap->s, &so, NULL)) == 0) { sopt.sopt_dir = SOPT_SET; sopt.sopt_level = uap->level; sopt.sopt_name = uap->name; sopt.sopt_val = uap->val; sopt.sopt_valsize = uap->valsize; sopt.sopt_td = td; error = sosetopt(so, &sopt); fputsock(so); } NET_UNLOCK_GIANT(); return(error); } /* * MPSAFE */ /* ARGSUSED */ int getsockopt(td, uap) struct thread *td; register struct getsockopt_args /* { int s; int level; int name; void * __restrict val; socklen_t * __restrict avalsize; } */ *uap; { socklen_t valsize; int error; struct socket *so; struct sockopt sopt; NET_LOCK_GIANT(); if ((error = fgetsock(td, uap->s, &so, NULL)) != 0) goto done2; if (uap->val) { error = copyin(uap->avalsize, &valsize, sizeof (valsize)); if (error) goto done1; if (valsize < 0) { error = EINVAL; goto done1; } } else { valsize = 0; } sopt.sopt_dir = SOPT_GET; sopt.sopt_level = uap->level; sopt.sopt_name = uap->name; sopt.sopt_val = uap->val; sopt.sopt_valsize = (size_t)valsize; /* checked non-negative above */ sopt.sopt_td = td; error = sogetopt(so, &sopt); if (error == 0) { valsize = sopt.sopt_valsize; error = copyout(&valsize, uap->avalsize, sizeof (valsize)); } done1: fputsock(so); done2: NET_UNLOCK_GIANT(); return (error); } /* * getsockname1() - Get socket name. * * MPSAFE */ /* ARGSUSED */ static int getsockname1(td, uap, compat) struct thread *td; register struct getsockname_args /* { int fdes; struct sockaddr * __restrict asa; socklen_t * __restrict alen; } */ *uap; int compat; { struct socket *so; struct sockaddr *sa; socklen_t len; int error; NET_LOCK_GIANT(); if ((error = fgetsock(td, uap->fdes, &so, NULL)) != 0) goto done2; error = copyin(uap->alen, &len, sizeof (len)); if (error) goto done1; if (len < 0) { error = EINVAL; goto done1; } sa = 0; error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, &sa); if (error) goto bad; if (sa == 0) { len = 0; goto gotnothing; } len = MIN(len, sa->sa_len); #ifdef COMPAT_OLDSOCK if (compat) ((struct osockaddr *)sa)->sa_family = sa->sa_family; #endif error = copyout(sa, uap->asa, (u_int)len); if (error == 0) gotnothing: error = copyout(&len, uap->alen, sizeof (len)); bad: if (sa) FREE(sa, M_SONAME); done1: fputsock(so); done2: NET_UNLOCK_GIANT(); return (error); } /* * MPSAFE */ int getsockname(td, uap) struct thread *td; struct getsockname_args *uap; { return (getsockname1(td, uap, 0)); } #ifdef COMPAT_OLDSOCK /* * MPSAFE */ int ogetsockname(td, uap) struct thread *td; struct getsockname_args *uap; { return (getsockname1(td, uap, 1)); } #endif /* COMPAT_OLDSOCK */ /* * getpeername1() - Get name of peer for connected socket. * * MPSAFE */ /* ARGSUSED */ static int getpeername1(td, uap, compat) struct thread *td; register struct getpeername_args /* { int fdes; struct sockaddr * __restrict asa; socklen_t * __restrict alen; } */ *uap; int compat; { struct socket *so; struct sockaddr *sa; socklen_t len; int error; NET_LOCK_GIANT(); if ((error = fgetsock(td, uap->fdes, &so, NULL)) != 0) goto done2; if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) { error = ENOTCONN; goto done1; } error = copyin(uap->alen, &len, sizeof (len)); if (error) goto done1; if (len < 0) { error = EINVAL; goto done1; } sa = 0; error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, &sa); if (error) goto bad; if (sa == 0) { len = 0; goto gotnothing; } len = MIN(len, sa->sa_len); #ifdef COMPAT_OLDSOCK if (compat) ((struct osockaddr *)sa)->sa_family = sa->sa_family; #endif error = copyout(sa, uap->asa, (u_int)len); if (error) goto bad; gotnothing: error = copyout(&len, uap->alen, sizeof (len)); bad: if (sa) FREE(sa, M_SONAME); done1: fputsock(so); done2: NET_UNLOCK_GIANT(); return (error); } /* * MPSAFE */ int getpeername(td, uap) struct thread *td; struct getpeername_args *uap; { return (getpeername1(td, uap, 0)); } #ifdef COMPAT_OLDSOCK /* * MPSAFE */ int ogetpeername(td, uap) struct thread *td; struct ogetpeername_args *uap; { /* XXX uap should have type `getpeername_args *' to begin with. */ return (getpeername1(td, (struct getpeername_args *)uap, 1)); } #endif /* COMPAT_OLDSOCK */ int sockargs(mp, buf, buflen, type) struct mbuf **mp; caddr_t buf; int buflen, type; { register struct sockaddr *sa; register struct mbuf *m; int error; if ((u_int)buflen > MLEN) { #ifdef COMPAT_OLDSOCK if (type == MT_SONAME && (u_int)buflen <= 112) buflen = MLEN; /* unix domain compat. hack */ else #endif if ((u_int)buflen > MCLBYTES) return (EINVAL); } m = m_get(M_TRYWAIT, type); if (m == NULL) return (ENOBUFS); if ((u_int)buflen > MLEN) { MCLGET(m, M_TRYWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); return (ENOBUFS); } } m->m_len = buflen; error = copyin(buf, mtod(m, caddr_t), (u_int)buflen); if (error) (void) m_free(m); else { *mp = m; if (type == MT_SONAME) { sa = mtod(m, struct sockaddr *); #if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN if (sa->sa_family == 0 && sa->sa_len < AF_MAX) sa->sa_family = sa->sa_len; #endif sa->sa_len = buflen; } } return (error); } int getsockaddr(namp, uaddr, len) struct sockaddr **namp; caddr_t uaddr; size_t len; { struct sockaddr *sa; int error; if (len > SOCK_MAXADDRLEN) return (ENAMETOOLONG); if (len < offsetof(struct sockaddr, sa_data[0])) return (EINVAL); MALLOC(sa, struct sockaddr *, len, M_SONAME, M_WAITOK); error = copyin(uaddr, sa, len); if (error) { FREE(sa, M_SONAME); } else { #if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN if (sa->sa_family == 0 && sa->sa_len < AF_MAX) sa->sa_family = sa->sa_len; #endif sa->sa_len = len; *namp = sa; } return (error); } /* * Detach mapped page and release resources back to the system. */ void sf_buf_mext(void *addr, void *args) { vm_page_t m; m = sf_buf_page(args); sf_buf_free(args); vm_page_lock_queues(); vm_page_unwire(m, 0); /* * Check for the object going away on us. This can * happen since we don't hold a reference to it. * If so, we're responsible for freeing the page. */ if (m->wire_count == 0 && m->object == NULL) vm_page_free(m); vm_page_unlock_queues(); } /* * sendfile(2) * * MPSAFE * * int sendfile(int fd, int s, off_t offset, size_t nbytes, * struct sf_hdtr *hdtr, off_t *sbytes, int flags) * * Send a file specified by 'fd' and starting at 'offset' to a socket * specified by 's'. Send only 'nbytes' of the file or until EOF if * nbytes == 0. Optionally add a header and/or trailer to the socket * output. If specified, write the total number of bytes sent into *sbytes. * */ int sendfile(struct thread *td, struct sendfile_args *uap) { return (do_sendfile(td, uap, 0)); } #ifdef COMPAT_FREEBSD4 int freebsd4_sendfile(struct thread *td, struct freebsd4_sendfile_args *uap) { struct sendfile_args args; args.fd = uap->fd; args.s = uap->s; args.offset = uap->offset; args.nbytes = uap->nbytes; args.hdtr = uap->hdtr; args.sbytes = uap->sbytes; args.flags = uap->flags; return (do_sendfile(td, &args, 1)); } #endif /* COMPAT_FREEBSD4 */ static int do_sendfile(struct thread *td, struct sendfile_args *uap, int compat) { struct vnode *vp; struct vm_object *obj; struct socket *so = NULL; struct mbuf *m, *m_header = NULL; struct sf_buf *sf; struct vm_page *pg; struct writev_args nuap; struct sf_hdtr hdtr; struct uio hdr_uio; off_t off, xfsize, hdtr_size, sbytes = 0; int error, s, headersize = 0, headersent = 0; struct iovec *hdr_iov = NULL; mtx_lock(&Giant); hdtr_size = 0; /* * The descriptor must be a regular file and have a backing VM object. */ if ((error = fgetvp_read(td, uap->fd, &vp)) != 0) goto done; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (vp->v_type != VREG || VOP_GETVOBJECT(vp, &obj) != 0) { error = EINVAL; VOP_UNLOCK(vp, 0, td); goto done; } VOP_UNLOCK(vp, 0, td); if ((error = fgetsock(td, uap->s, &so, NULL)) != 0) goto done; if (so->so_type != SOCK_STREAM) { error = EINVAL; goto done; } if ((so->so_state & SS_ISCONNECTED) == 0) { error = ENOTCONN; goto done; } if (uap->offset < 0) { error = EINVAL; goto done; } #ifdef MAC error = mac_check_socket_send(td->td_ucred, so); if (error) goto done; #endif /* * If specified, get the pointer to the sf_hdtr struct for * any headers/trailers. */ if (uap->hdtr != NULL) { error = copyin(uap->hdtr, &hdtr, sizeof(hdtr)); if (error) goto done; /* * Send any headers. */ if (hdtr.headers != NULL) { hdr_uio.uio_td = td; hdr_uio.uio_rw = UIO_WRITE; error = uiofromiov(hdtr.headers, hdtr.hdr_cnt, &hdr_uio); if (error) goto done; /* Cache hdr_iov, m_uiotombuf may change it. */ hdr_iov = hdr_uio.uio_iov; if (hdr_uio.uio_resid > 0) { m_header = m_uiotombuf(&hdr_uio, M_DONTWAIT, 0); if (m_header == NULL) goto done; headersize = m_header->m_pkthdr.len; if (compat) sbytes += headersize; } } } /* * Protect against multiple writers to the socket. */ (void) sblock(&so->so_snd, M_WAITOK); /* * Loop through the pages in the file, starting with the requested * offset. Get a file page (do I/O if necessary), map the file page * into an sf_buf, attach an mbuf header to the sf_buf, and queue * it on the socket. */ for (off = uap->offset; ; off += xfsize, sbytes += xfsize) { vm_pindex_t pindex; vm_offset_t pgoff; pindex = OFF_TO_IDX(off); VM_OBJECT_LOCK(obj); retry_lookup: /* * Calculate the amount to transfer. Not to exceed a page, * the EOF, or the passed in nbytes. */ xfsize = obj->un_pager.vnp.vnp_size - off; VM_OBJECT_UNLOCK(obj); if (xfsize > PAGE_SIZE) xfsize = PAGE_SIZE; pgoff = (vm_offset_t)(off & PAGE_MASK); if (PAGE_SIZE - pgoff < xfsize) xfsize = PAGE_SIZE - pgoff; if (uap->nbytes && xfsize > (uap->nbytes - sbytes)) xfsize = uap->nbytes - sbytes; if (xfsize <= 0) { if (m_header != NULL) { m = m_header; m_header = NULL; goto retry_space; } else break; } /* * Optimize the non-blocking case by looking at the socket space * before going to the extra work of constituting the sf_buf. */ if ((so->so_state & SS_NBIO) && sbspace(&so->so_snd) <= 0) { if (so->so_state & SS_CANTSENDMORE) error = EPIPE; else error = EAGAIN; sbunlock(&so->so_snd); goto done; } VM_OBJECT_LOCK(obj); /* * Attempt to look up the page. * * Allocate if not found * * Wait and loop if busy. */ pg = vm_page_lookup(obj, pindex); if (pg == NULL) { pg = vm_page_alloc(obj, pindex, VM_ALLOC_NORMAL | VM_ALLOC_WIRED); if (pg == NULL) { VM_OBJECT_UNLOCK(obj); VM_WAIT; VM_OBJECT_LOCK(obj); goto retry_lookup; } vm_page_lock_queues(); vm_page_wakeup(pg); } else { vm_page_lock_queues(); if (vm_page_sleep_if_busy(pg, TRUE, "sfpbsy")) goto retry_lookup; /* * Wire the page so it does not get ripped out from * under us. */ vm_page_wire(pg); } /* * If page is not valid for what we need, initiate I/O */ if (pg->valid && vm_page_is_valid(pg, pgoff, xfsize)) { VM_OBJECT_UNLOCK(obj); } else if (uap->flags & SF_NODISKIO) { error = EBUSY; } else { int bsize, resid; /* * Ensure that our page is still around when the I/O * completes. */ vm_page_io_start(pg); vm_page_unlock_queues(); VM_OBJECT_UNLOCK(obj); /* * Get the page from backing store. */ bsize = vp->v_mount->mnt_stat.f_iosize; vn_lock(vp, LK_SHARED | LK_NOPAUSE | LK_RETRY, td); /* * XXXMAC: Because we don't have fp->f_cred here, * we pass in NOCRED. This is probably wrong, but * is consistent with our original implementation. */ error = vn_rdwr(UIO_READ, vp, NULL, MAXBSIZE, trunc_page(off), UIO_NOCOPY, IO_NODELOCKED | IO_VMIO | ((MAXBSIZE / bsize) << IO_SEQSHIFT), td->td_ucred, NOCRED, &resid, td); VOP_UNLOCK(vp, 0, td); if (error) VM_OBJECT_LOCK(obj); vm_page_lock_queues(); vm_page_io_finish(pg); mbstat.sf_iocnt++; } if (error) { vm_page_unwire(pg, 0); /* * See if anyone else might know about this page. * If not and it is not valid, then free it. */ if (pg->wire_count == 0 && pg->valid == 0 && pg->busy == 0 && !(pg->flags & PG_BUSY) && pg->hold_count == 0) { vm_page_busy(pg); vm_page_free(pg); } vm_page_unlock_queues(); VM_OBJECT_UNLOCK(obj); sbunlock(&so->so_snd); goto done; } vm_page_unlock_queues(); /* * Get a sendfile buf. We usually wait as long as necessary, * but this wait can be interrupted. */ if ((sf = sf_buf_alloc(pg, PCATCH)) == NULL) { mbstat.sf_allocfail++; vm_page_lock_queues(); vm_page_unwire(pg, 0); if (pg->wire_count == 0 && pg->object == NULL) vm_page_free(pg); vm_page_unlock_queues(); sbunlock(&so->so_snd); error = EINTR; goto done; } /* * Get an mbuf header and set it up as having external storage. */ if (m_header) MGET(m, M_TRYWAIT, MT_DATA); else MGETHDR(m, M_TRYWAIT, MT_DATA); if (m == NULL) { error = ENOBUFS; sf_buf_mext((void *)sf_buf_kva(sf), sf); sbunlock(&so->so_snd); goto done; } /* * Setup external storage for mbuf. */ MEXTADD(m, sf_buf_kva(sf), PAGE_SIZE, sf_buf_mext, sf, M_RDONLY, EXT_SFBUF); m->m_data = (char *)sf_buf_kva(sf) + pgoff; m->m_pkthdr.len = m->m_len = xfsize; if (m_header) { m_cat(m_header, m); m = m_header; m_header = NULL; m_fixhdr(m); } /* * Add the buffer to the socket buffer chain. */ s = splnet(); retry_space: /* * Make sure that the socket is still able to take more data. * CANTSENDMORE being true usually means that the connection * was closed. so_error is true when an error was sensed after * a previous send. * The state is checked after the page mapping and buffer * allocation above since those operations may block and make * any socket checks stale. From this point forward, nothing * blocks before the pru_send (or more accurately, any blocking * results in a loop back to here to re-check). */ if ((so->so_state & SS_CANTSENDMORE) || so->so_error) { if (so->so_state & SS_CANTSENDMORE) { error = EPIPE; } else { error = so->so_error; so->so_error = 0; } m_freem(m); sbunlock(&so->so_snd); splx(s); goto done; } /* * Wait for socket space to become available. We do this just * after checking the connection state above in order to avoid * a race condition with sbwait(). */ if (sbspace(&so->so_snd) < so->so_snd.sb_lowat) { if (so->so_state & SS_NBIO) { m_freem(m); sbunlock(&so->so_snd); splx(s); error = EAGAIN; goto done; } error = sbwait(&so->so_snd); /* * An error from sbwait usually indicates that we've * been interrupted by a signal. If we've sent anything * then return bytes sent, otherwise return the error. */ if (error) { m_freem(m); sbunlock(&so->so_snd); splx(s); goto done; } goto retry_space; } error = (*so->so_proto->pr_usrreqs->pru_send)(so, 0, m, 0, 0, td); splx(s); if (error) { sbunlock(&so->so_snd); goto done; } headersent = 1; } sbunlock(&so->so_snd); /* * Send trailers. Wimp out and use writev(2). */ if (uap->hdtr != NULL && hdtr.trailers != NULL) { nuap.fd = uap->s; nuap.iovp = hdtr.trailers; nuap.iovcnt = hdtr.trl_cnt; error = writev(td, &nuap); if (error) goto done; if (compat) sbytes += td->td_retval[0]; else hdtr_size += td->td_retval[0]; } done: if (headersent) { if (!compat) hdtr_size += headersize; } else { if (compat) sbytes -= headersize; } /* * If there was no error we have to clear td->td_retval[0] * because it may have been set by writev. */ if (error == 0) { td->td_retval[0] = 0; } if (uap->sbytes != NULL) { if (!compat) sbytes += hdtr_size; copyout(&sbytes, uap->sbytes, sizeof(off_t)); } if (vp) vrele(vp); if (so) fputsock(so); if (hdr_iov) FREE(hdr_iov, M_IOV); if (m_header) m_freem(m_header); mtx_unlock(&Giant); if (error == ERESTART) error = EINTR; return (error); } Index: head/sys/kern/vfs_extattr.c =================================================================== --- head/sys/kern/vfs_extattr.c (revision 130343) +++ head/sys/kern/vfs_extattr.c (revision 130344) @@ -1,4814 +1,4814 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 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. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int chroot_refuse_vdir_fds(struct filedesc *fdp); static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); static int setfown(struct thread *td, struct vnode *, uid_t, gid_t); static int setfmode(struct thread *td, struct vnode *, int); static int setfflags(struct thread *td, struct vnode *, int); static int setutimes(struct thread *td, struct vnode *, const struct timespec *, int, int); static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred, struct thread *td); static int extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, size_t nbytes, struct thread *td); int (*union_dircheckp)(struct thread *td, struct vnode **, struct file *); int (*softdep_fsync_hook)(struct vnode *); /* * The module initialization routine for POSIX asynchronous I/O will * set this to the version of AIO that it implements. (Zero means * that it is not implemented.) This value is used here by pathconf() * and in kern_descrip.c by fpathconf(). */ int async_io_version; /* * Sync each mounted filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct sync_args { int dummy; }; #endif #ifdef DEBUG static int syncprt = 0; SYSCTL_INT(_debug, OID_AUTO, syncprt, CTLFLAG_RW, &syncprt, 0, ""); #endif /* ARGSUSED */ int sync(td, uap) struct thread *td; struct sync_args *uap; { struct mount *mp, *nmp; int asyncflag; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if ((mp->mnt_flag & MNT_RDONLY) == 0 && vn_start_write(NULL, &mp, V_NOWAIT) == 0) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; vfs_msync(mp, MNT_NOWAIT); VFS_SYNC(mp, MNT_NOWAIT, ((td != NULL) ? td->td_ucred : NOCRED), td); mp->mnt_flag |= asyncflag; vn_finished_write(mp); } mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); } mtx_unlock(&mountlist_mtx); #if 0 /* * XXX don't call vfs_bufstats() yet because that routine * was not imported in the Lite2 merge. */ #ifdef DIAGNOSTIC if (syncprt) vfs_bufstats(); #endif /* DIAGNOSTIC */ #endif return (0); } /* XXX PRISON: could be per prison flag */ static int prison_quotas; #if 0 SYSCTL_INT(_kern_prison, OID_AUTO, quotas, CTLFLAG_RW, &prison_quotas, 0, ""); #endif /* * Change filesystem quotas. */ #ifndef _SYS_SYSPROTO_H_ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; #endif int quotactl(td, uap) struct thread *td; register struct quotactl_args /* { char *path; int cmd; int uid; caddr_t arg; } */ *uap; { struct mount *mp, *vmp; int error; struct nameidata nd; if (jailed(td->td_ucred) && !prison_quotas) return (EPERM); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_start_write(nd.ni_vp, &vmp, V_WAIT | PCATCH); mp = nd.ni_vp->v_mount; vrele(nd.ni_vp); if (error) return (error); error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, td); vn_finished_write(vmp); return (error); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct statfs_args { char *path; struct statfs *buf; }; #endif int statfs(td, uap) struct thread *td; register struct statfs_args /* { char *path; struct statfs *buf; } */ *uap; { struct mount *mp; struct statfs *sp, sb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif /* * Set these in case the underlying filesystem fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp, td); if (error) return (error); if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout(sp, uap->buf, sizeof(*sp))); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct fstatfs_args { int fd; struct statfs *buf; }; #endif int fstatfs(td, uap) struct thread *td; register struct fstatfs_args /* { int fd; struct statfs *buf; } */ *uap; { struct file *fp; struct mount *mp; struct statfs *sp, sb; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); mp = fp->f_vnode->v_mount; fdrop(fp, td); if (mp == NULL) return (EBADF); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif sp = &mp->mnt_stat; /* * Set these in case the underlying filesystem fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp, td); if (error) return (error); if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout(sp, uap->buf, sizeof(*sp))); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct getfsstat_args { struct statfs *buf; long bufsize; int flags; }; #endif int getfsstat(td, uap) struct thread *td; register struct getfsstat_args /* { struct statfs *buf; long bufsize; int flags; } */ *uap; { struct mount *mp, *nmp; struct statfs *sp, sb; caddr_t sfsp; long count, maxcount, error; maxcount = uap->bufsize / sizeof(struct statfs); sfsp = (caddr_t)uap->buf; count = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (!prison_check_mount(td->td_ucred, mp)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #ifdef MAC if (mac_check_mount_stat(td->td_ucred, mp) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #endif if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if (sfsp && count < maxcount) { sp = &mp->mnt_stat; /* * Set these in case the underlying filesystem * fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; /* * If MNT_NOWAIT or MNT_LAZY is specified, do not * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY * overrides MNT_WAIT. */ if (((uap->flags & (MNT_LAZY|MNT_NOWAIT)) == 0 || (uap->flags & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, td))) { mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); continue; } if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } error = copyout(sp, sfsp, sizeof(*sp)); if (error) { vfs_unbusy(mp, td); return (error); } sfsp += sizeof(*sp); } count++; mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); } mtx_unlock(&mountlist_mtx); if (sfsp && count > maxcount) td->td_retval[0] = maxcount; else td->td_retval[0] = count; return (0); } #ifdef COMPAT_FREEBSD4 /* * Get old format filesystem statistics. */ static void cvtstatfs(struct thread *, struct statfs *, struct ostatfs *); #ifndef _SYS_SYSPROTO_H_ struct freebsd4_statfs_args { char *path; struct ostatfs *buf; }; #endif int freebsd4_statfs(td, uap) struct thread *td; struct freebsd4_statfs_args /* { char *path; struct ostatfs *buf; } */ *uap; { struct mount *mp; struct statfs *sp; struct ostatfs osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif error = VFS_STATFS(mp, sp, td); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; cvtstatfs(td, sp, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_fstatfs_args { int fd; struct ostatfs *buf; }; #endif int freebsd4_fstatfs(td, uap) struct thread *td; struct freebsd4_fstatfs_args /* { int fd; struct ostatfs *buf; } */ *uap; { struct file *fp; struct mount *mp; struct statfs *sp; struct ostatfs osb; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); mp = fp->f_vnode->v_mount; fdrop(fp, td); if (mp == NULL) return (EBADF); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif sp = &mp->mnt_stat; error = VFS_STATFS(mp, sp, td); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; cvtstatfs(td, sp, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_getfsstat_args { struct ostatfs *buf; long bufsize; int flags; }; #endif int freebsd4_getfsstat(td, uap) struct thread *td; register struct freebsd4_getfsstat_args /* { struct ostatfs *buf; long bufsize; int flags; } */ *uap; { struct mount *mp, *nmp; struct statfs *sp; struct ostatfs osb; caddr_t sfsp; long count, maxcount, error; maxcount = uap->bufsize / sizeof(struct ostatfs); sfsp = (caddr_t)uap->buf; count = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (!prison_check_mount(td->td_ucred, mp)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #ifdef MAC if (mac_check_mount_stat(td->td_ucred, mp) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #endif if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if (sfsp && count < maxcount) { sp = &mp->mnt_stat; /* * If MNT_NOWAIT or MNT_LAZY is specified, do not * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY * overrides MNT_WAIT. */ if (((uap->flags & (MNT_LAZY|MNT_NOWAIT)) == 0 || (uap->flags & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, td))) { mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); continue; } sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; cvtstatfs(td, sp, &osb); error = copyout(&osb, sfsp, sizeof(osb)); if (error) { vfs_unbusy(mp, td); return (error); } sfsp += sizeof(osb); } count++; mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); } mtx_unlock(&mountlist_mtx); if (sfsp && count > maxcount) td->td_retval[0] = maxcount; else td->td_retval[0] = count; return (0); } /* * Implement fstatfs() for (NFS) file handles. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_fhstatfs_args { struct fhandle *u_fhp; struct ostatfs *buf; }; #endif int freebsd4_fhstatfs(td, uap) struct thread *td; struct freebsd4_fhstatfs_args /* { struct fhandle *u_fhp; struct ostatfs *buf; } */ *uap; { struct statfs *sp; struct mount *mp; struct vnode *vp; struct ostatfs osb; fhandle_t fh; int error; error = suser(td); if (error) return (error); if ((error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t))) != 0) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif if ((error = VFS_STATFS(mp, sp, td)) != 0) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; cvtstatfs(td, sp, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); } /* * Convert a new format statfs structure to an old format statfs structure. */ static void cvtstatfs(td, nsp, osp) struct thread *td; struct statfs *nsp; struct ostatfs *osp; { bzero(osp, sizeof(*osp)); osp->f_bsize = MIN(nsp->f_bsize, LONG_MAX); osp->f_iosize = MIN(nsp->f_iosize, LONG_MAX); osp->f_blocks = MIN(nsp->f_blocks, LONG_MAX); osp->f_bfree = MIN(nsp->f_bfree, LONG_MAX); osp->f_bavail = MIN(nsp->f_bavail, LONG_MAX); osp->f_files = MIN(nsp->f_files, LONG_MAX); osp->f_ffree = MIN(nsp->f_ffree, LONG_MAX); osp->f_owner = nsp->f_owner; osp->f_type = nsp->f_type; osp->f_flags = nsp->f_flags; osp->f_syncwrites = MIN(nsp->f_syncwrites, LONG_MAX); osp->f_asyncwrites = MIN(nsp->f_asyncwrites, LONG_MAX); osp->f_syncreads = MIN(nsp->f_syncreads, LONG_MAX); osp->f_asyncreads = MIN(nsp->f_asyncreads, LONG_MAX); bcopy(nsp->f_fstypename, osp->f_fstypename, MIN(MFSNAMELEN, OMNAMELEN)); bcopy(nsp->f_mntonname, osp->f_mntonname, MIN(MFSNAMELEN, OMNAMELEN)); bcopy(nsp->f_mntfromname, osp->f_mntfromname, MIN(MFSNAMELEN, OMNAMELEN)); if (suser(td)) { osp->f_fsid.val[0] = osp->f_fsid.val[1] = 0; } else { osp->f_fsid = nsp->f_fsid; } } #endif /* COMPAT_FREEBSD4 */ /* * Change current working directory to a given file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchdir_args { int fd; }; #endif int fchdir(td, uap) struct thread *td; struct fchdir_args /* { int fd; } */ *uap; { register struct filedesc *fdp = td->td_proc->p_fd; struct vnode *vp, *tdp, *vpold; struct mount *mp; struct file *fp; int error; if ((error = getvnode(fdp, uap->fd, &fp)) != 0) return (error); vp = fp->f_vnode; VREF(vp); fdrop(fp, td); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (vp->v_type != VDIR) error = ENOTDIR; #ifdef MAC else if ((error = mac_check_vnode_chdir(td->td_ucred, vp)) != 0) { } #endif else error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); while (!error && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, 0, 0, td)) continue; error = VFS_ROOT(mp, &tdp); vfs_unbusy(mp, td); if (error) break; vput(vp); vp = tdp; } if (error) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, td); FILEDESC_LOCK(fdp); vpold = fdp->fd_cdir; fdp->fd_cdir = vp; FILEDESC_UNLOCK(fdp); vrele(vpold); return (0); } /* * Change current working directory (``.''). */ #ifndef _SYS_SYSPROTO_H_ struct chdir_args { char *path; }; #endif int chdir(td, uap) struct thread *td; struct chdir_args /* { char *path; } */ *uap; { return (kern_chdir(td, uap->path, UIO_USERSPACE)); } int kern_chdir(struct thread *td, char *path, enum uio_seg pathseg) { register struct filedesc *fdp = td->td_proc->p_fd; int error; struct nameidata nd; struct vnode *vp; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); if ((error = change_dir(nd.ni_vp, td)) != 0) { vput(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); return (error); } VOP_UNLOCK(nd.ni_vp, 0, td); NDFREE(&nd, NDF_ONLY_PNBUF); FILEDESC_LOCK(fdp); vp = fdp->fd_cdir; fdp->fd_cdir = nd.ni_vp; FILEDESC_UNLOCK(fdp); vrele(vp); return (0); } /* * Helper function for raised chroot(2) security function: Refuse if * any filedescriptors are open directories. */ static int chroot_refuse_vdir_fds(fdp) struct filedesc *fdp; { struct vnode *vp; struct file *fp; int fd; FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); for (fd = 0; fd < fdp->fd_nfiles ; fd++) { fp = fget_locked(fdp, fd); if (fp == NULL) continue; if (fp->f_type == DTYPE_VNODE) { vp = fp->f_vnode; if (vp->v_type == VDIR) return (EPERM); } } return (0); } /* * This sysctl determines if we will allow a process to chroot(2) if it * has a directory open: * 0: disallowed for all processes. * 1: allowed for processes that were not already chroot(2)'ed. * 2: allowed for all processes. */ static int chroot_allow_open_directories = 1; SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW, &chroot_allow_open_directories, 0, ""); /* * Change notion of root (``/'') directory. */ #ifndef _SYS_SYSPROTO_H_ struct chroot_args { char *path; }; #endif int chroot(td, uap) struct thread *td; struct chroot_args /* { char *path; } */ *uap; { int error; struct nameidata nd; error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, td); mtx_lock(&Giant); error = namei(&nd); if (error) goto error; if ((error = change_dir(nd.ni_vp, td)) != 0) goto e_vunlock; #ifdef MAC if ((error = mac_check_vnode_chroot(td->td_ucred, nd.ni_vp))) goto e_vunlock; #endif VOP_UNLOCK(nd.ni_vp, 0, td); error = change_root(nd.ni_vp, td); vrele(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); mtx_unlock(&Giant); return (error); e_vunlock: vput(nd.ni_vp); error: mtx_unlock(&Giant); NDFREE(&nd, NDF_ONLY_PNBUF); return (error); } /* * Common routine for chroot and chdir. Callers must provide a locked vnode * instance. */ int change_dir(vp, td) struct vnode *vp; struct thread *td; { int error; ASSERT_VOP_LOCKED(vp, "change_dir(): vp not locked"); if (vp->v_type != VDIR) return (ENOTDIR); #ifdef MAC error = mac_check_vnode_chdir(td->td_ucred, vp); if (error) return (error); #endif error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); return (error); } /* * Common routine for kern_chroot() and jail_attach(). The caller is * responsible for invoking suser() and mac_check_chroot() to authorize this * operation. */ int change_root(vp, td) struct vnode *vp; struct thread *td; { struct filedesc *fdp; struct vnode *oldvp; int error; mtx_assert(&Giant, MA_OWNED); fdp = td->td_proc->p_fd; FILEDESC_LOCK(fdp); if (chroot_allow_open_directories == 0 || (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { error = chroot_refuse_vdir_fds(fdp); if (error) { FILEDESC_UNLOCK(fdp); return (error); } } oldvp = fdp->fd_rdir; fdp->fd_rdir = vp; VREF(fdp->fd_rdir); if (!fdp->fd_jdir) { fdp->fd_jdir = vp; VREF(fdp->fd_jdir); } FILEDESC_UNLOCK(fdp); vrele(oldvp); return (0); } /* * Check permissions, allocate an open file structure, * and call the device open routine if any. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct open_args { char *path; int flags; int mode; }; #endif int open(td, uap) struct thread *td; register struct open_args /* { char *path; int flags; int mode; } */ *uap; { return (kern_open(td, uap->path, UIO_USERSPACE, uap->flags, uap->mode)); } int kern_open(struct thread *td, char *path, enum uio_seg pathseg, int flags, int mode) { struct proc *p = td->td_proc; struct filedesc *fdp = p->p_fd; struct file *fp; struct vnode *vp; struct vattr vat; struct mount *mp; int cmode; struct file *nfp; int type, indx, error; struct flock lf; struct nameidata nd; if ((flags & O_ACCMODE) == O_ACCMODE) return (EINVAL); flags = FFLAGS(flags); error = falloc(td, &nfp, &indx); if (error) return (error); /* An extra reference on `nfp' has been held for us by falloc(). */ fp = nfp; cmode = ((mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); td->td_dupfd = -1; /* XXX check for fdopen */ mtx_lock(&Giant); error = vn_open(&nd, &flags, cmode, indx); if (error) { mtx_unlock(&Giant); /* * If the vn_open replaced the method vector, something * wonderous happened deep below and we just pass it up * pretending we know what we do. */ if (error == ENXIO && fp->f_ops != &badfileops) { fdrop(fp, td); td->td_retval[0] = indx; return (0); } /* * release our own reference */ fdrop(fp, td); /* * handle special fdopen() case. bleh. dupfdopen() is * responsible for dropping the old contents of ofiles[indx] * if it succeeds. */ if ((error == ENODEV || error == ENXIO) && td->td_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(td, fdp, indx, td->td_dupfd, flags, error)) == 0) { td->td_retval[0] = indx; return (0); } /* * Clean up the descriptor, but only if another thread hadn't * replaced or closed it. */ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdunused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else { FILEDESC_UNLOCK(fdp); } if (error == ERESTART) error = EINTR; return (error); } td->td_dupfd = 0; NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; /* * There should be 2 references on the file, one from the descriptor * table, and one for us. * * Handle the case where someone closed the file (via its file * descriptor) while we were blocked. The end result should look * like opening the file succeeded but it was immediately closed. * We call vn_close() manually because we haven't yet hooked up * the various 'struct file' fields. */ FILEDESC_LOCK(fdp); FILE_LOCK(fp); if (fp->f_count == 1) { KASSERT(fdp->fd_ofiles[indx] != fp, ("Open file descriptor lost all refs")); FILEDESC_UNLOCK(fdp); FILE_UNLOCK(fp); VOP_UNLOCK(vp, 0, td); vn_close(vp, flags & FMASK, fp->f_cred, td); mtx_unlock(&Giant); fdrop(fp, td); td->td_retval[0] = indx; return (0); } fp->f_vnode = vp; fp->f_data = vp; fp->f_flag = flags & FMASK; fp->f_ops = &vnops; fp->f_seqcount = 1; fp->f_type = (vp->v_type == VFIFO ? DTYPE_FIFO : DTYPE_VNODE); FILEDESC_UNLOCK(fdp); FILE_UNLOCK(fp); /* assert that vn_open created a backing object if one is needed */ KASSERT(!vn_canvmio(vp) || VOP_GETVOBJECT(vp, NULL) == 0, ("open: vmio vnode has no backing object after vn_open")); VOP_UNLOCK(vp, 0, td); if (flags & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (flags & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((flags & FNONBLOCK) == 0) type |= F_WAIT; if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) goto bad; fp->f_flag |= FHASLOCK; } if (flags & O_TRUNC) { if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) goto bad; VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); VATTR_NULL(&vat); vat.va_size = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); #ifdef MAC error = mac_check_vnode_write(td->td_ucred, fp->f_cred, vp); if (error == 0) #endif error = VOP_SETATTR(vp, &vat, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); if (error) goto bad; } mtx_unlock(&Giant); /* * Release our private reference, leaving the one associated with * the descriptor table intact. */ fdrop(fp, td); td->td_retval[0] = indx; return (0); bad: mtx_unlock(&Giant); FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdunused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else { FILEDESC_UNLOCK(fdp); } fdrop(fp, td); return (error); } #ifdef COMPAT_43 /* * Create a file. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct ocreat_args { char *path; int mode; }; #endif int ocreat(td, uap) struct thread *td; register struct ocreat_args /* { char *path; int mode; } */ *uap; { return (kern_open(td, uap->path, UIO_USERSPACE, O_WRONLY | O_CREAT | O_TRUNC, uap->mode)); } #endif /* COMPAT_43 */ /* * Create a special file. */ #ifndef _SYS_SYSPROTO_H_ struct mknod_args { char *path; int mode; int dev; }; #endif int mknod(td, uap) struct thread *td; register struct mknod_args /* { char *path; int mode; int dev; } */ *uap; { return (kern_mknod(td, uap->path, UIO_USERSPACE, uap->mode, uap->dev)); } int kern_mknod(struct thread *td, char *path, enum uio_seg pathseg, int mode, int dev) { struct vnode *vp; struct mount *mp; struct vattr vattr; int error; int whiteout = 0; struct nameidata nd; switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: error = suser(td); break; default: error = suser_cred(td->td_ucred, PRISON_ROOT); break; } if (error) return (error); restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); if (vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (EEXIST); } else { VATTR_NULL(&vattr); FILEDESC_LOCK(td->td_proc->p_fd); vattr.va_mode = (mode & ALLPERMS) & ~td->td_proc->p_fd->fd_cmask; FILEDESC_UNLOCK(td->td_proc->p_fd); vattr.va_rdev = dev; whiteout = 0; switch (mode & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ vattr.va_type = VBAD; break; case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; case S_IFWHT: whiteout = 1; break; default: error = EINVAL; break; } } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } #ifdef MAC if (error == 0 && !whiteout) error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); #endif if (!error) { VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); if (whiteout) error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); else { error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); } } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mknod"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mknod"); return (error); } /* * Create a named pipe. */ #ifndef _SYS_SYSPROTO_H_ struct mkfifo_args { char *path; int mode; }; #endif int mkfifo(td, uap) struct thread *td; register struct mkfifo_args /* { char *path; int mode; } */ *uap; { return (kern_mkfifo(td, uap->path, UIO_USERSPACE, uap->mode)); } int kern_mkfifo(struct thread *td, char *path, enum uio_seg pathseg, int mode) { struct mount *mp; struct vattr vattr; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VFIFO; FILEDESC_LOCK(td->td_proc->p_fd); vattr.va_mode = (mode & ALLPERMS) & ~td->td_proc->p_fd->fd_cmask; FILEDESC_UNLOCK(td->td_proc->p_fd); #ifdef MAC error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error) goto out; #endif VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); #ifdef MAC out: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); return (error); } /* * Make a hard file link. */ #ifndef _SYS_SYSPROTO_H_ struct link_args { char *path; char *link; }; #endif int link(td, uap) struct thread *td; register struct link_args /* { char *path; char *link; } */ *uap; { return (kern_link(td, uap->path, uap->link, UIO_USERSPACE)); } SYSCTL_DECL(_security_bsd); static int hardlink_check_uid = 0; SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, &hardlink_check_uid, 0, "Unprivileged processes cannot create hard links to files owned by other " "users"); static int hardlink_check_gid = 0; SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_gid, CTLFLAG_RW, &hardlink_check_gid, 0, "Unprivileged processes cannot create hard links to files owned by other " "groups"); static int can_hardlink(struct vnode *vp, struct thread *td, struct ucred *cred) { struct vattr va; int error; if (suser_cred(cred, PRISON_ROOT) == 0) return (0); if (!hardlink_check_uid && !hardlink_check_gid) return (0); error = VOP_GETATTR(vp, &va, cred, td); if (error != 0) return (error); if (hardlink_check_uid) { if (cred->cr_uid != va.va_uid) return (EPERM); } if (hardlink_check_gid) { if (!groupmember(va.va_gid, cred)) return (EPERM); } return (0); } int kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg) { struct vnode *vp; struct mount *mp; struct nameidata nd; int error; bwillwrite(); NDINIT(&nd, LOOKUP, FOLLOW|NOOBJ, segflg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type == VDIR) { vrele(vp); return (EPERM); /* POSIX */ } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } NDINIT(&nd, CREATE, LOCKPARENT | NOOBJ | SAVENAME, segflg, link, td); if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) { vrele(nd.ni_vp); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); error = EEXIST; } else if ((error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td)) == 0) { VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); error = can_hardlink(vp, td, td->td_ucred); if (error == 0) #ifdef MAC error = mac_check_vnode_link(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); if (error == 0) #endif error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); VOP_UNLOCK(vp, 0, td); vput(nd.ni_dvp); } NDFREE(&nd, NDF_ONLY_PNBUF); } vrele(vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "link"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "link"); return (error); } /* * Make a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct symlink_args { char *path; char *link; }; #endif int symlink(td, uap) struct thread *td; register struct symlink_args /* { char *path; char *link; } */ *uap; { return (kern_symlink(td, uap->path, uap->link, UIO_USERSPACE)); } int kern_symlink(struct thread *td, char *path, char *link, enum uio_seg segflg) { struct mount *mp; struct vattr vattr; char *syspath; int error; struct nameidata nd; if (segflg == UIO_SYSSPACE) { syspath = path; } else { syspath = uma_zalloc(namei_zone, M_WAITOK); if ((error = copyinstr(path, syspath, MAXPATHLEN, NULL)) != 0) goto out; } restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT | NOOBJ | SAVENAME, segflg, link, td); if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); error = EEXIST; goto out; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) goto out; goto restart; } VATTR_NULL(&vattr); FILEDESC_LOCK(td->td_proc->p_fd); vattr.va_mode = ACCESSPERMS &~ td->td_proc->p_fd->fd_cmask; FILEDESC_UNLOCK(td->td_proc->p_fd); #ifdef MAC vattr.va_type = VLNK; error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error) goto out2; #endif VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, syspath); if (error == 0) vput(nd.ni_vp); #ifdef MAC out2: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "symlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "symlink"); out: if (segflg != UIO_SYSSPACE) uma_zfree(namei_zone, syspath); return (error); } /* * Delete a whiteout from the filesystem. */ int undelete(td, uap) struct thread *td; register struct undelete_args /* { char *path; } */ *uap; { int error; struct mount *mp; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp) vrele(nd.ni_vp); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "undelete"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "undelete"); return (error); } /* * Delete a name from the filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct unlink_args { char *path; }; #endif int unlink(td, uap) struct thread *td; struct unlink_args /* { char *path; } */ *uap; { return (kern_unlink(td, uap->path, UIO_USERSPACE)); } int kern_unlink(struct thread *td, char *path, enum uio_seg pathseg) { struct mount *mp; struct vnode *vp; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT|LOCKLEAF, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { /* * The root of a mounted filesystem cannot be deleted. * * XXX: can this only be a VDIR case? */ if (vp->v_vflag & VV_ROOT) error = EBUSY; } if (error == 0) { if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); if (vp == nd.ni_dvp) vrele(vp); else vput(vp); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } #ifdef MAC error = mac_check_vnode_delete(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); if (error) goto out; #endif VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); #ifdef MAC out: #endif vn_finished_write(mp); } NDFREE(&nd, NDF_ONLY_PNBUF); if (vp == nd.ni_dvp) vrele(vp); else vput(vp); vput(nd.ni_dvp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "unlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "unlink"); return (error); } /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct lseek_args { int fd; int pad; off_t offset; int whence; }; #endif int lseek(td, uap) struct thread *td; register struct lseek_args /* { int fd; int pad; off_t offset; int whence; } */ *uap; { struct ucred *cred = td->td_ucred; struct file *fp; struct vnode *vp; struct vattr vattr; off_t offset; int error, noneg; if ((error = fget(td, uap->fd, &fp)) != 0) return (error); if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) { fdrop(fp, td); return (ESPIPE); } vp = fp->f_vnode; noneg = (vp->v_type != VCHR); offset = uap->offset; switch (uap->whence) { case L_INCR: if (noneg && (fp->f_offset < 0 || (offset > 0 && fp->f_offset > OFF_MAX - offset))) { error = EOVERFLOW; break; } offset += fp->f_offset; break; case L_XTND: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = VOP_GETATTR(vp, &vattr, cred, td); VOP_UNLOCK(vp, 0, td); if (error) break; if (noneg && (vattr.va_size > OFF_MAX || (offset > 0 && vattr.va_size > OFF_MAX - offset))) { error = EOVERFLOW; break; } offset += vattr.va_size; break; case L_SET: break; default: error = EINVAL; } if (error == 0 && noneg && offset < 0) error = EINVAL; if (error != 0) { fdrop(fp, td); return (error); } fp->f_offset = offset; *(off_t *)(td->td_retval) = fp->f_offset; fdrop(fp, td); return (0); } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct olseek_args { int fd; long offset; int whence; }; #endif int olseek(td, uap) struct thread *td; register struct olseek_args /* { int fd; long offset; int whence; } */ *uap; { struct lseek_args /* { int fd; int pad; off_t offset; int whence; } */ nuap; int error; nuap.fd = uap->fd; nuap.offset = uap->offset; nuap.whence = uap->whence; error = lseek(td, &nuap); return (error); } #endif /* COMPAT_43 */ /* * Check access permissions using passed credentials. */ static int vn_access(vp, user_flags, cred, td) struct vnode *vp; int user_flags; struct ucred *cred; struct thread *td; { int error, flags; /* Flags == 0 means only check for existence. */ error = 0; if (user_flags) { flags = 0; if (user_flags & R_OK) flags |= VREAD; if (user_flags & W_OK) flags |= VWRITE; if (user_flags & X_OK) flags |= VEXEC; #ifdef MAC error = mac_check_vnode_access(cred, vp, flags); if (error) return (error); #endif if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, flags, cred, td); } return (error); } /* * Check access permissions using "real" credentials. */ #ifndef _SYS_SYSPROTO_H_ struct access_args { char *path; int flags; }; #endif int access(td, uap) struct thread *td; register struct access_args /* { char *path; int flags; } */ *uap; { return (kern_access(td, uap->path, UIO_USERSPACE, uap->flags)); } int kern_access(struct thread *td, char *path, enum uio_seg pathseg, int flags) { struct ucred *cred, *tmpcred; register struct vnode *vp; int error; struct nameidata nd; /* * Create and modify a temporary credential instead of one that * is potentially shared. This could also mess up socket * buffer accounting which can run in an interrupt context. */ cred = td->td_ucred; tmpcred = crdup(cred); tmpcred->cr_uid = cred->cr_ruid; tmpcred->cr_groups[0] = cred->cr_rgid; td->td_ucred = tmpcred; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, pathseg, path, td); if ((error = namei(&nd)) != 0) goto out1; vp = nd.ni_vp; error = vn_access(vp, flags, tmpcred, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); out1: td->td_ucred = cred; crfree(tmpcred); return (error); } /* * Check access permissions using "effective" credentials. */ #ifndef _SYS_SYSPROTO_H_ struct eaccess_args { char *path; int flags; }; #endif int eaccess(td, uap) struct thread *td; register struct eaccess_args /* { char *path; int flags; } */ *uap; { struct nameidata nd; struct vnode *vp; int error; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_access(vp, uap->flags, td->td_ucred, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); return (error); } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct ostat_args { char *path; struct ostat *ub; }; #endif int ostat(td, uap) struct thread *td; register struct ostat_args /* { char *path; struct ostat *ub; } */ *uap; { struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); vput(nd.ni_vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout(&osb, uap->ub, sizeof (osb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct olstat_args { char *path; struct ostat *ub; }; #endif int olstat(td, uap) struct thread *td; register struct olstat_args /* { char *path; struct ostat *ub; } */ *uap; { struct vnode *vp; struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout(&osb, uap->ub, sizeof (osb)); return (error); } /* * Convert from an old to a new stat structure. */ void cvtstat(st, ost) struct stat *st; struct ostat *ost; { ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (st->st_size < (quad_t)1 << 32) ost->st_size = st->st_size; else ost->st_size = -2; ost->st_atime = st->st_atime; ost->st_mtime = st->st_mtime; ost->st_ctime = st->st_ctime; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct stat_args { char *path; struct stat *ub; }; #endif int stat(td, uap) struct thread *td; register struct stat_args /* { char *path; struct stat *ub; } */ *uap; { struct stat sb; int error; struct nameidata nd; #ifdef LOOKUP_SHARED NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); #else NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); #endif if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); if (error) return (error); error = copyout(&sb, uap->ub, sizeof (sb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif int lstat(td, uap) struct thread *td; register struct lstat_args /* { char *path; struct stat *ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); error = copyout(&sb, uap->ub, sizeof (sb)); return (error); } /* * Implementation of the NetBSD stat() function. * XXX This should probably be collapsed with the FreeBSD version, * as the differences are only due to vn_stat() clearing spares at * the end of the structures. vn_stat could be split to avoid this, * and thus collapse the following to close to zero code. */ void cvtnstat(sb, nsb) struct stat *sb; struct nstat *nsb; { bzero(nsb, sizeof *nsb); nsb->st_dev = sb->st_dev; nsb->st_ino = sb->st_ino; nsb->st_mode = sb->st_mode; nsb->st_nlink = sb->st_nlink; nsb->st_uid = sb->st_uid; nsb->st_gid = sb->st_gid; nsb->st_rdev = sb->st_rdev; nsb->st_atimespec = sb->st_atimespec; nsb->st_mtimespec = sb->st_mtimespec; nsb->st_ctimespec = sb->st_ctimespec; nsb->st_size = sb->st_size; nsb->st_blocks = sb->st_blocks; nsb->st_blksize = sb->st_blksize; nsb->st_flags = sb->st_flags; nsb->st_gen = sb->st_gen; nsb->st_birthtimespec = sb->st_birthtimespec; } #ifndef _SYS_SYSPROTO_H_ struct nstat_args { char *path; struct nstat *ub; }; #endif int nstat(td, uap) struct thread *td; register struct nstat_args /* { char *path; struct nstat *ub; } */ *uap; { struct stat sb; struct nstat nsb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); vput(nd.ni_vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout(&nsb, uap->ub, sizeof (nsb)); return (error); } /* * NetBSD lstat. Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif int nlstat(td, uap) struct thread *td; register struct nlstat_args /* { char *path; struct nstat *ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nstat nsb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); vput(vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout(&nsb, uap->ub, sizeof (nsb)); return (error); } /* * Get configurable pathname variables. */ #ifndef _SYS_SYSPROTO_H_ struct pathconf_args { char *path; int name; }; #endif int pathconf(td, uap) struct thread *td; register struct pathconf_args /* { char *path; int name; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); /* If asynchronous I/O is available, it works for all files. */ if (uap->name == _PC_ASYNC_IO) td->td_retval[0] = async_io_version; else error = VOP_PATHCONF(nd.ni_vp, uap->name, td->td_retval); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct readlink_args { char *path; char *buf; int count; }; #endif int readlink(td, uap) struct thread *td; register struct readlink_args /* { char *path; char *buf; int count; } */ *uap; { return (kern_readlink(td, uap->path, UIO_USERSPACE, uap->buf, UIO_USERSPACE, uap->count)); } int kern_readlink(struct thread *td, char *path, enum uio_seg pathseg, char *buf, enum uio_seg bufseg, int count) { register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; #ifdef MAC error = mac_check_vnode_readlink(td->td_ucred, vp); if (error) { vput(vp); return (error); } #endif if (vp->v_type != VLNK) error = EINVAL; else { aiov.iov_base = buf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = bufseg; auio.uio_td = td; auio.uio_resid = count; error = VOP_READLINK(vp, &auio, td->td_ucred); } vput(vp); td->td_retval[0] = count - auio.uio_resid; return (error); } /* * Common implementation code for chflags() and fchflags(). */ static int setfflags(td, vp, flags) struct thread *td; struct vnode *vp; int flags; { int error; struct mount *mp; struct vattr vattr; /* * Prevent non-root users from setting flags on devices. When * a device is reused, users can retain ownership of the device * if they are allowed to set flags and programs assume that * chown can't fail when done as root. */ if (vp->v_type == VCHR || vp->v_type == VBLK) { error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VATTR_NULL(&vattr); vattr.va_flags = flags; #ifdef MAC error = mac_check_vnode_setflags(td->td_ucred, vp, vattr.va_flags); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } /* * Change flags of a file given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chflags_args { char *path; int flags; }; #endif int chflags(td, uap) struct thread *td; register struct chflags_args /* { char *path; int flags; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(td, nd.ni_vp, uap->flags); vrele(nd.ni_vp); return (error); } /* * Same as chflags() but doesn't follow symlinks. */ int lchflags(td, uap) struct thread *td; register struct lchflags_args /* { char *path; int flags; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(td, nd.ni_vp, uap->flags); vrele(nd.ni_vp); return (error); } /* * Change flags of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchflags_args { int fd; int flags; }; #endif int fchflags(td, uap) struct thread *td; register struct fchflags_args /* { int fd; int flags; } */ *uap; { struct file *fp; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); error = setfflags(td, fp->f_vnode, uap->flags); fdrop(fp, td); return (error); } /* * Common implementation code for chmod(), lchmod() and fchmod(). */ static int setfmode(td, vp, mode) struct thread *td; struct vnode *vp; int mode; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VATTR_NULL(&vattr); vattr.va_mode = mode & ALLPERMS; #ifdef MAC error = mac_check_vnode_setmode(td->td_ucred, vp, vattr.va_mode); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } /* * Change mode of a file given path name. */ #ifndef _SYS_SYSPROTO_H_ struct chmod_args { char *path; int mode; }; #endif int chmod(td, uap) struct thread *td; register struct chmod_args /* { char *path; int mode; } */ *uap; { return (kern_chmod(td, uap->path, UIO_USERSPACE, uap->mode)); } int kern_chmod(struct thread *td, char *path, enum uio_seg pathseg, int mode) { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(td, nd.ni_vp, mode); vrele(nd.ni_vp); return (error); } /* * Change mode of a file given path name (don't follow links.) */ #ifndef _SYS_SYSPROTO_H_ struct lchmod_args { char *path; int mode; }; #endif int lchmod(td, uap) struct thread *td; register struct lchmod_args /* { char *path; int mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(td, nd.ni_vp, uap->mode); vrele(nd.ni_vp); return (error); } /* * Change mode of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchmod_args { int fd; int mode; }; #endif int fchmod(td, uap) struct thread *td; register struct fchmod_args /* { int fd; int mode; } */ *uap; { struct file *fp; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); error = setfmode(td, fp->f_vnode, uap->mode); fdrop(fp, td); return (error); } /* * Common implementation for chown(), lchown(), and fchown() */ static int setfown(td, vp, uid, gid) struct thread *td; struct vnode *vp; uid_t uid; gid_t gid; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VATTR_NULL(&vattr); vattr.va_uid = uid; vattr.va_gid = gid; #ifdef MAC error = mac_check_vnode_setowner(td->td_ucred, vp, vattr.va_uid, vattr.va_gid); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } /* * Set ownership given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chown_args { char *path; int uid; int gid; }; #endif int chown(td, uap) struct thread *td; register struct chown_args /* { char *path; int uid; int gid; } */ *uap; { return (kern_chown(td, uap->path, UIO_USERSPACE, uap->uid, uap->gid)); } int kern_chown(struct thread *td, char *path, enum uio_seg pathseg, int uid, int gid) { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(td, nd.ni_vp, uid, gid); vrele(nd.ni_vp); return (error); } /* * Set ownership given a path name, do not cross symlinks. */ #ifndef _SYS_SYSPROTO_H_ struct lchown_args { char *path; int uid; int gid; }; #endif int lchown(td, uap) struct thread *td; register struct lchown_args /* { char *path; int uid; int gid; } */ *uap; { return (kern_lchown(td, uap->path, UIO_USERSPACE, uap->uid, uap->gid)); } int kern_lchown(struct thread *td, char *path, enum uio_seg pathseg, int uid, int gid) { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(td, nd.ni_vp, uid, gid); vrele(nd.ni_vp); return (error); } /* * Set ownership given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchown_args { int fd; int uid; int gid; }; #endif int fchown(td, uap) struct thread *td; register struct fchown_args /* { int fd; int uid; int gid; } */ *uap; { struct file *fp; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); error = setfown(td, fp->f_vnode, uap->uid, uap->gid); fdrop(fp, td); return (error); } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int getutimes(usrtvp, tvpseg, tsp) const struct timeval *usrtvp; enum uio_seg tvpseg; struct timespec *tsp; { struct timeval tv[2]; const struct timeval *tvp; int error; if (usrtvp == NULL) { microtime(&tv[0]); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); tsp[1] = tsp[0]; } else { if (tvpseg == UIO_SYSSPACE) { tvp = usrtvp; } else { if ((error = copyin(usrtvp, tv, sizeof(tv))) != 0) return (error); tvp = tv; } TIMEVAL_TO_TIMESPEC(&tvp[0], &tsp[0]); TIMEVAL_TO_TIMESPEC(&tvp[1], &tsp[1]); } return (0); } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int setutimes(td, vp, ts, numtimes, nullflag) struct thread *td; struct vnode *vp; const struct timespec *ts; int numtimes; int nullflag; { int error, setbirthtime; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); setbirthtime = 0; if (numtimes < 3 && VOP_GETATTR(vp, &vattr, td->td_ucred, td) == 0 && timespeccmp(&ts[1], &vattr.va_birthtime, < )) setbirthtime = 1; VATTR_NULL(&vattr); vattr.va_atime = ts[0]; vattr.va_mtime = ts[1]; if (setbirthtime) vattr.va_birthtime = ts[1]; if (numtimes > 2) vattr.va_birthtime = ts[2]; if (nullflag) vattr.va_vaflags |= VA_UTIMES_NULL; #ifdef MAC error = mac_check_vnode_setutimes(td->td_ucred, vp, vattr.va_atime, vattr.va_mtime); #endif if (error == 0) error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct utimes_args { char *path; struct timeval *tptr; }; #endif int utimes(td, uap) struct thread *td; register struct utimes_args /* { char *path; struct timeval *tptr; } */ *uap; { return (kern_utimes(td, uap->path, UIO_USERSPACE, uap->tptr, UIO_USERSPACE)); } int kern_utimes(struct thread *td, char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; int error; struct nameidata nd; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(td, nd.ni_vp, ts, 2, tptr == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct lutimes_args { char *path; struct timeval *tptr; }; #endif int lutimes(td, uap) struct thread *td; register struct lutimes_args /* { char *path; struct timeval *tptr; } */ *uap; { return (kern_lutimes(td, uap->path, UIO_USERSPACE, uap->tptr, UIO_USERSPACE)); } int kern_lutimes(struct thread *td, char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; int error; struct nameidata nd; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(td, nd.ni_vp, ts, 2, tptr == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct futimes_args { int fd; struct timeval *tptr; }; #endif int futimes(td, uap) struct thread *td; register struct futimes_args /* { int fd; struct timeval *tptr; } */ *uap; { return (kern_futimes(td, uap->fd, uap->tptr, UIO_USERSPACE)); } int kern_futimes(struct thread *td, int fd, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; struct file *fp; int error; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); if ((error = getvnode(td->td_proc->p_fd, fd, &fp)) != 0) return (error); error = setutimes(td, fp->f_vnode, ts, 2, tptr == NULL); fdrop(fp, td); return (error); } /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct truncate_args { char *path; int pad; off_t length; }; #endif int truncate(td, uap) struct thread *td; register struct truncate_args /* { char *path; int pad; off_t length; } */ *uap; { return (kern_truncate(td, uap->path, UIO_USERSPACE, uap->length)); } int kern_truncate(struct thread *td, char *path, enum uio_seg pathseg, off_t length) { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (vp->v_type == VDIR) error = EISDIR; #ifdef MAC else if ((error = mac_check_vnode_write(td->td_ucred, NOCRED, vp))) { } #endif else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td)) == 0) { VATTR_NULL(&vattr); vattr.va_size = length; error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); } vput(vp); vn_finished_write(mp); return (error); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct ftruncate_args { int fd; int pad; off_t length; }; #endif int ftruncate(td, uap) struct thread *td; register struct ftruncate_args /* { int fd; int pad; off_t length; } */ *uap; { struct mount *mp; struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (uap->length < 0) return(EINVAL); if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); if ((fp->f_flag & FWRITE) == 0) { fdrop(fp, td); return (EINVAL); } vp = fp->f_vnode; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { fdrop(fp, td); return (error); } VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (vp->v_type == VDIR) error = EISDIR; #ifdef MAC else if ((error = mac_check_vnode_write(td->td_ucred, fp->f_cred, vp))) { } #endif else if ((error = vn_writechk(vp)) == 0) { VATTR_NULL(&vattr); vattr.va_size = uap->length; error = VOP_SETATTR(vp, &vattr, fp->f_cred, td); } VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); fdrop(fp, td); return (error); } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct otruncate_args { char *path; long length; }; #endif int otruncate(td, uap) struct thread *td; register struct otruncate_args /* { char *path; long length; } */ *uap; { struct truncate_args /* { char *path; int pad; off_t length; } */ nuap; nuap.path = uap->path; nuap.length = uap->length; return (truncate(td, &nuap)); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct oftruncate_args { int fd; long length; }; #endif int oftruncate(td, uap) struct thread *td; register struct oftruncate_args /* { int fd; long length; } */ *uap; { struct ftruncate_args /* { int fd; int pad; off_t length; } */ nuap; nuap.fd = uap->fd; nuap.length = uap->length; return (ftruncate(td, &nuap)); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ /* * Sync an open file. */ #ifndef _SYS_SYSPROTO_H_ struct fsync_args { int fd; }; #endif int fsync(td, uap) struct thread *td; struct fsync_args /* { int fd; } */ *uap; { struct vnode *vp; struct mount *mp; struct file *fp; vm_object_t obj; int error; GIANT_REQUIRED; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); vp = fp->f_vnode; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { fdrop(fp, td); return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (VOP_GETVOBJECT(vp, &obj) == 0) { VM_OBJECT_LOCK(obj); vm_object_page_clean(obj, 0, 0, 0); VM_OBJECT_UNLOCK(obj); } error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, td); if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP) && softdep_fsync_hook != NULL) error = (*softdep_fsync_hook)(vp); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); fdrop(fp, td); return (error); } /* * Rename files. Source and destination must either both be directories, * or both not be directories. If target is a directory, it must be empty. */ #ifndef _SYS_SYSPROTO_H_ struct rename_args { char *from; char *to; }; #endif int rename(td, uap) struct thread *td; register struct rename_args /* { char *from; char *to; } */ *uap; { return (kern_rename(td, uap->from, uap->to, UIO_USERSPACE)); } int kern_rename(struct thread *td, char *from, char *to, enum uio_seg pathseg) { struct mount *mp = NULL; struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; bwillwrite(); #ifdef MAC NDINIT(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART, pathseg, from, td); #else NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, pathseg, from, td); #endif if ((error = namei(&fromnd)) != 0) return (error); #ifdef MAC error = mac_check_vnode_rename_from(td->td_ucred, fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd); VOP_UNLOCK(fromnd.ni_dvp, 0, td); VOP_UNLOCK(fromnd.ni_vp, 0, td); #endif fvp = fromnd.ni_vp; if (error == 0) error = vn_start_write(fvp, &mp, V_WAIT | PCATCH); if (error != 0) { NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | NOOBJ, pathseg, to, td); if (fromnd.ni_vp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&tond)) != 0) { /* Translate error code for rename("dir1", "dir2/."). */ if (error == EISDIR && fvp->v_type == VDIR) error = EINVAL; NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } if (fvp == tdvp) error = EINVAL; /* * If the source is the same as the destination (that is, if they * are links to the same vnode), then there is nothing to do. */ if (fvp == tvp) error = -1; #ifdef MAC else error = mac_check_vnode_rename_to(td->td_ucred, tdvp, tond.ni_vp, fromnd.ni_dvp == tdvp, &tond.ni_cnd); #endif out: if (!error) { VOP_LEASE(tdvp, td, td->td_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) { VOP_LEASE(fromnd.ni_dvp, td, td->td_ucred, LEASE_WRITE); } if (tvp) { VOP_LEASE(tvp, td, td->td_ucred, LEASE_WRITE); } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); } else { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); ASSERT_VOP_UNLOCKED(fromnd.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(fromnd.ni_vp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_vp, "rename"); out1: vn_finished_write(mp); if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); if (error == -1) return (0); return (error); } /* * Make a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct mkdir_args { char *path; int mode; }; #endif int mkdir(td, uap) struct thread *td; register struct mkdir_args /* { char *path; int mode; } */ *uap; { return (kern_mkdir(td, uap->path, UIO_USERSPACE, uap->mode)); } int kern_mkdir(struct thread *td, char *path, enum uio_seg segflg, int mode) { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME, segflg, path, td); nd.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); /* * XXX namei called with LOCKPARENT but not LOCKLEAF has * the strange behaviour of leaving the vnode unlocked * if the target is the same vnode as the parent. */ if (vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VDIR; FILEDESC_LOCK(td->td_proc->p_fd); vattr.va_mode = (mode & ACCESSPERMS) &~ td->td_proc->p_fd->fd_cmask; FILEDESC_UNLOCK(td->td_proc->p_fd); #ifdef MAC error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error) goto out; #endif VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); #ifdef MAC out: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (!error) vput(nd.ni_vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mkdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mkdir"); return (error); } /* * Remove a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct rmdir_args { char *path; }; #endif int rmdir(td, uap) struct thread *td; struct rmdir_args /* { char *path; } */ *uap; { return (kern_rmdir(td, uap->path, UIO_USERSPACE)); } int kern_rmdir(struct thread *td, char *path, enum uio_seg pathseg) { struct mount *mp; struct vnode *vp; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_vflag & VV_ROOT) { error = EBUSY; goto out; } #ifdef MAC error = mac_check_vnode_delete(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); if (error) goto out; #endif if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); vn_finished_write(mp); out: NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "rmdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "rmdir"); return (error); } #ifdef COMPAT_43 /* * Read a block of directory entries in a filesystem independent format. */ #ifndef _SYS_SYSPROTO_H_ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int ogetdirentries(td, uap) struct thread *td; register struct ogetdirentries_args /* { int fd; char *buf; u_int count; long *basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; int error, eofflag, readcnt; long loff; /* XXX arbitrary sanity limit on `count'. */ if (uap->count > 64 * 1024) return (EINVAL); if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); return (EBADF); } vp = fp->f_vnode; unionread: if (vp->v_type != VDIR) { fdrop(fp, td); return (EINVAL); } aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; auio.uio_resid = uap->count; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); loff = auio.uio_offset = fp->f_offset; #ifdef MAC error = mac_check_vnode_readdir(td->td_ucred, vp); if (error) { VOP_UNLOCK(vp, 0, td); fdrop(fp, td); return (error); } #endif # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kuio.uio_segflg = UIO_SYSSPACE; kiov.iov_len = uap->count; MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = uap->count - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } FREE(dirbuf, M_TEMP); } VOP_UNLOCK(vp, 0, td); if (error) { fdrop(fp, td); return (error); } if (uap->count == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(td, &vp, fp); if (error == -1) goto unionread; if (error) { fdrop(fp, td); return (error); } } /* * XXX We could delay dropping the lock above but * union_dircheckp complicates things. */ vn_lock(vp, LK_EXCLUSIVE|LK_RETRY, td); if ((vp->v_vflag & VV_ROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_vnode = vp; fp->f_data = vp; fp->f_offset = 0; vput(tvp); goto unionread; } VOP_UNLOCK(vp, 0, td); } error = copyout(&loff, uap->basep, sizeof(long)); fdrop(fp, td); td->td_retval[0] = uap->count - auio.uio_resid; return (error); } #endif /* COMPAT_43 */ /* * Read a block of directory entries in a filesystem independent format. */ #ifndef _SYS_SYSPROTO_H_ struct getdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int getdirentries(td, uap) struct thread *td; register struct getdirentries_args /* { int fd; char *buf; u_int count; long *basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error, eofflag; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); return (EBADF); } vp = fp->f_vnode; unionread: if (vp->v_type != VDIR) { fdrop(fp, td); return (EINVAL); } aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; auio.uio_resid = uap->count; /* vn_lock(vp, LK_SHARED | LK_RETRY, td); */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); loff = auio.uio_offset = fp->f_offset; #ifdef MAC error = mac_check_vnode_readdir(td->td_ucred, vp); if (error == 0) #endif error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp, 0, td); if (error) { fdrop(fp, td); return (error); } if (uap->count == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(td, &vp, fp); if (error == -1) goto unionread; if (error) { fdrop(fp, td); return (error); } } /* * XXX We could delay dropping the lock above but * union_dircheckp complicates things. */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if ((vp->v_vflag & VV_ROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_vnode = vp; fp->f_data = vp; fp->f_offset = 0; vput(tvp); goto unionread; } VOP_UNLOCK(vp, 0, td); } if (uap->basep != NULL) { error = copyout(&loff, uap->basep, sizeof(long)); } td->td_retval[0] = uap->count - auio.uio_resid; fdrop(fp, td); return (error); } #ifndef _SYS_SYSPROTO_H_ struct getdents_args { int fd; char *buf; size_t count; }; #endif int getdents(td, uap) struct thread *td; register struct getdents_args /* { int fd; char *buf; u_int count; } */ *uap; { struct getdirentries_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; ap.basep = NULL; return (getdirentries(td, &ap)); } /* * Set the mode mask for creation of filesystem nodes. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct umask_args { int newmask; }; #endif int umask(td, uap) struct thread *td; struct umask_args /* { int newmask; } */ *uap; { register struct filedesc *fdp; FILEDESC_LOCK(td->td_proc->p_fd); fdp = td->td_proc->p_fd; td->td_retval[0] = fdp->fd_cmask; fdp->fd_cmask = uap->newmask & ALLPERMS; FILEDESC_UNLOCK(td->td_proc->p_fd); return (0); } /* * Void all references to file by ripping underlying filesystem * away from vnode. */ #ifndef _SYS_SYSPROTO_H_ struct revoke_args { char *path; }; #endif int revoke(td, uap) struct thread *td; register struct revoke_args /* { char *path; } */ *uap; { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VCHR) { vput(vp); return (EINVAL); } #ifdef MAC error = mac_check_vnode_revoke(td->td_ucred, vp); if (error) { vput(vp); return (error); } #endif error = VOP_GETATTR(vp, &vattr, td->td_ucred, td); if (error) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, td); if (td->td_ucred->cr_uid != vattr.va_uid) { error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) goto out; } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) goto out; if (vcount(vp) > 1) VOP_REVOKE(vp, REVOKEALL); vn_finished_write(mp); out: vrele(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry. * A reference on the file entry is held upon returning. */ int getvnode(fdp, fd, fpp) struct filedesc *fdp; int fd; struct file **fpp; { int error; struct file *fp; fp = NULL; if (fdp == NULL) error = EBADF; else { FILEDESC_LOCK(fdp); if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) error = EBADF; else if (fp->f_vnode == NULL) { fp = NULL; error = EINVAL; } else { fhold(fp); error = 0; } FILEDESC_UNLOCK(fdp); } *fpp = fp; return (error); } /* * Get (NFS) file handle */ #ifndef _SYS_SYSPROTO_H_ struct lgetfh_args { char *fname; fhandle_t *fhp; }; #endif int lgetfh(td, uap) struct thread *td; register struct lgetfh_args *uap; { struct nameidata nd; fhandle_t fh; register struct vnode *vp; int error; error = suser(td); if (error) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error) return (error); error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } #ifndef _SYS_SYSPROTO_H_ struct getfh_args { char *fname; fhandle_t *fhp; }; #endif int getfh(td, uap) struct thread *td; register struct getfh_args *uap; { struct nameidata nd; fhandle_t fh; register struct vnode *vp; int error; error = suser(td); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error) return (error); error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } /* * syscall for the rpc.lockd to use to translate a NFS file handle into * an open descriptor. * * warning: do not remove the suser() call or this becomes one giant * security hole. */ #ifndef _SYS_SYSPROTO_H_ struct fhopen_args { const struct fhandle *u_fhp; int flags; }; #endif int fhopen(td, uap) struct thread *td; struct fhopen_args /* { const struct fhandle *u_fhp; int flags; } */ *uap; { struct proc *p = td->td_proc; struct mount *mp; struct vnode *vp; struct fhandle fhp; struct vattr vat; struct vattr *vap = &vat; struct flock lf; struct file *fp; register struct filedesc *fdp = p->p_fd; int fmode, mode, error, type; struct file *nfp; int indx; error = suser(td); if (error) return (error); fmode = FFLAGS(uap->flags); /* why not allow a non-read/write open for our lockd? */ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); error = copyin(uap->u_fhp, &fhp, sizeof(fhp)); if (error) return(error); /* find the mount point */ mp = vfs_getvfs(&fhp.fh_fsid); if (mp == NULL) return (ESTALE); /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, &vp); if (error) return (error); /* * from now on we have to make sure not * to forget about the vnode * any error that causes an abort must vput(vp) * just set error = err and 'goto bad;'. */ /* * from vn_open */ if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (fmode & O_APPEND) mode |= VAPPEND; #ifdef MAC error = mac_check_vnode_open(td->td_ucred, vp, mode); if (error) goto bad; #endif if (mode) { error = VOP_ACCESS(vp, mode, td->td_ucred, td); if (error) goto bad; } if (fmode & O_TRUNC) { VOP_UNLOCK(vp, 0, td); /* XXX */ if ((error = vn_start_write(NULL, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); /* XXX */ #ifdef MAC /* * We don't yet have fp->f_cred, so use td->td_ucred, which * should be right. */ error = mac_check_vnode_write(td->td_ucred, td->td_ucred, vp); if (error == 0) { #endif VATTR_NULL(vap); vap->va_size = 0; error = VOP_SETATTR(vp, vap, td->td_ucred, td); #ifdef MAC } #endif vn_finished_write(mp); if (error) goto bad; } error = VOP_OPEN(vp, fmode, td->td_ucred, td, -1); if (error) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, td, td->td_ucred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; /* * end of vn_open code */ if ((error = falloc(td, &nfp, &indx)) != 0) { if (fmode & FWRITE) vp->v_writecount--; goto bad; } /* An extra reference on `nfp' has been held for us by falloc(). */ fp = nfp; nfp->f_vnode = vp; nfp->f_data = vp; nfp->f_flag = fmode & FMASK; nfp->f_ops = &vnops; nfp->f_type = DTYPE_VNODE; if (fmode & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (fmode & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((fmode & FNONBLOCK) == 0) type |= F_WAIT; VOP_UNLOCK(vp, 0, td); if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) { /* * The lock request failed. Normally close the * descriptor but handle the case where someone might * have dup()d or close()d it when we weren't looking. */ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdunused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else { FILEDESC_UNLOCK(fdp); } /* * release our private reference */ fdrop(fp, td); return(error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); fp->f_flag |= FHASLOCK; } if ((vp->v_type == VREG) && (VOP_GETVOBJECT(vp, NULL) != 0)) vfs_object_create(vp, td, td->td_ucred); VOP_UNLOCK(vp, 0, td); fdrop(fp, td); td->td_retval[0] = indx; return (0); bad: vput(vp); return (error); } /* * Stat an (NFS) file handle. */ #ifndef _SYS_SYSPROTO_H_ struct fhstat_args { struct fhandle *u_fhp; struct stat *sb; }; #endif int fhstat(td, uap) struct thread *td; register struct fhstat_args /* { struct fhandle *u_fhp; struct stat *sb; } */ *uap; { struct stat sb; fhandle_t fh; struct mount *mp; struct vnode *vp; int error; error = suser(td); if (error) return (error); error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); if (error) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); vput(vp); if (error) return (error); error = copyout(&sb, uap->sb, sizeof(sb)); return (error); } /* * Implement fstatfs() for (NFS) file handles. */ #ifndef _SYS_SYSPROTO_H_ struct fhstatfs_args { struct fhandle *u_fhp; struct statfs *buf; }; #endif int fhstatfs(td, uap) struct thread *td; struct fhstatfs_args /* { struct fhandle *u_fhp; struct statfs *buf; } */ *uap; { struct statfs *sp; struct mount *mp; struct vnode *vp; fhandle_t fh; int error; error = suser(td); if (error) return (error); if ((error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t))) != 0) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif /* * Set these in case the underlying filesystem fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if ((error = VFS_STATFS(mp, sp, td)) != 0) return (error); return (copyout(sp, uap->buf, sizeof(*sp))); } /* * Syscall to push extended attribute configuration information into the * VFS. Accepts a path, which it converts to a mountpoint, as well as * a command (int cmd), and attribute name and misc data. For now, the * attribute name is left in userspace for consumption by the VFS_op. * It will probably be changed to be copied into sysspace by the * syscall in the future, once issues with various consumers of the * attribute code have raised their hands. * * Currently this is used only by UFS Extended Attributes. */ int extattrctl(td, uap) struct thread *td; struct extattrctl_args /* { const char *path; int cmd; const char *filename; int attrnamespace; const char *attrname; } */ *uap; { struct vnode *filename_vp; struct nameidata nd; struct mount *mp, *mp_writable; char attrname[EXTATTR_MAXNAMELEN]; int error; /* * uap->attrname is not always defined. We check again later when we * invoke the VFS call so as to pass in NULL there if needed. */ if (uap->attrname != NULL) { error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); } /* * uap->filename is not always defined. If it is, grab a vnode lock, * which VFS_EXTATTRCTL() will later release. */ filename_vp = NULL; if (uap->filename != NULL) { NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->filename, td); error = namei(&nd); if (error) return (error); filename_vp = nd.ni_vp; NDFREE(&nd, NDF_NO_VP_RELE | NDF_NO_VP_UNLOCK); } /* uap->path is always defined. */ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) { if (filename_vp != NULL) vput(filename_vp); return (error); } mp = nd.ni_vp->v_mount; error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | PCATCH); NDFREE(&nd, 0); if (error) { if (filename_vp != NULL) vput(filename_vp); return (error); } error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace, uap->attrname != NULL ? attrname : NULL, td); vn_finished_write(mp_writable); /* * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, * filename_vp, so vrele it if it is defined. */ if (filename_vp != NULL) vrele(filename_vp); return (error); } /*- * Set a named extended attribute on a file or directory * * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", * kernelspace string pointer "attrname", userspace buffer * pointer "data", buffer length "nbytes", thread "td". * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ static int extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname, void *data, size_t nbytes, struct thread *td) { struct mount *mp; struct uio auio; struct iovec aiov; ssize_t cnt; int error; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); aiov.iov_base = data; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; if (nbytes > INT_MAX) { error = EINVAL; goto done; } auio.uio_resid = nbytes; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; cnt = nbytes; #ifdef MAC error = mac_check_vnode_setextattr(td->td_ucred, vp, attrnamespace, attrname, &auio); if (error) goto done; #endif error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, td->td_ucred, td); cnt -= auio.uio_resid; td->td_retval[0] = cnt; done: VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } int extattr_set_fd(td, uap) struct thread *td; struct extattr_set_fd_args /* { int fd; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); error = getvnode(td->td_proc->p_fd, uap->fd, &fp); if (error) return (error); error = extattr_set_vp(fp->f_vnode, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); fdrop(fp, td); return (error); } int extattr_set_file(td, uap) struct thread *td; struct extattr_set_file_args /* { const char *path; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } int extattr_set_link(td, uap) struct thread *td; struct extattr_set_link_args /* { const char *path; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } /*- * Get a named extended attribute on a file or directory * * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", * kernelspace string pointer "attrname", userspace buffer * pointer "data", buffer length "nbytes", thread "td". * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ static int extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, void *data, size_t nbytes, struct thread *td) { struct uio auio, *auiop; struct iovec aiov; ssize_t cnt; size_t size, *sizep; int error; /* * XXX: Temporary API compatibility for applications that know * about this hack ("" means list), but haven't been updated * for the extattr_list_*() system calls yet. This will go * away for FreeBSD 5.3. */ if (strlen(attrname) == 0) return (extattr_list_vp(vp, attrnamespace, data, nbytes, td)); VOP_LEASE(vp, td, td->td_ucred, LEASE_READ); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); /* * Slightly unusual semantics: if the user provides a NULL data * pointer, they don't want to receive the data, just the * maximum read length. */ auiop = NULL; sizep = NULL; cnt = 0; if (data != NULL) { aiov.iov_base = data; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_offset = 0; if (nbytes > INT_MAX) { error = EINVAL; goto done; } auio.uio_resid = nbytes; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; auiop = &auio; cnt = nbytes; } else { sizep = &size; } #ifdef MAC error = mac_check_vnode_getextattr(td->td_ucred, vp, attrnamespace, attrname, &auio); if (error) goto done; #endif error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep, td->td_ucred, td); if (auiop != NULL) { cnt -= auio.uio_resid; td->td_retval[0] = cnt; } else { td->td_retval[0] = size; } done: VOP_UNLOCK(vp, 0, td); return (error); } int extattr_get_fd(td, uap) struct thread *td; struct extattr_get_fd_args /* { int fd; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); error = getvnode(td->td_proc->p_fd, uap->fd, &fp); if (error) return (error); error = extattr_get_vp(fp->f_vnode, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); fdrop(fp, td); return (error); } int extattr_get_file(td, uap) struct thread *td; struct extattr_get_file_args /* { const char *path; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } int extattr_get_link(td, uap) struct thread *td; struct extattr_get_link_args /* { const char *path; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } /* * extattr_delete_vp(): Delete a named extended attribute on a file or * directory * * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", * kernelspace string pointer "attrname", proc "p" * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ static int extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, struct thread *td) { struct mount *mp; int error; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); #ifdef MAC error = mac_check_vnode_deleteextattr(td->td_ucred, vp, attrnamespace, attrname); if (error) goto done; #endif error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred, td); if (error == EOPNOTSUPP) error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, td->td_ucred, td); #ifdef MAC done: #endif VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } int extattr_delete_fd(td, uap) struct thread *td; struct extattr_delete_fd_args /* { int fd; int attrnamespace; const char *attrname; } */ *uap; { struct file *fp; struct vnode *vp; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); error = getvnode(td->td_proc->p_fd, uap->fd, &fp); if (error) return (error); vp = fp->f_vnode; error = extattr_delete_vp(vp, uap->attrnamespace, attrname, td); fdrop(fp, td); return (error); } int extattr_delete_file(td, uap) struct thread *td; struct extattr_delete_file_args /* { const char *path; int attrnamespace; const char *attrname; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return(error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return(error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); vrele(nd.ni_vp); return(error); } int extattr_delete_link(td, uap) struct thread *td; struct extattr_delete_link_args /* { const char *path; int attrnamespace; const char *attrname; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return(error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return(error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); vrele(nd.ni_vp); return(error); } /*- * Retrieve a list of extended attributes on a file or directory. * * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace", * userspace buffer pointer "data", buffer length "nbytes", * thread "td". * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ static int extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, size_t nbytes, struct thread *td) { struct uio auio, *auiop; size_t size, *sizep; struct iovec aiov; ssize_t cnt; int error; VOP_LEASE(vp, td, td->td_ucred, LEASE_READ); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); auiop = NULL; sizep = NULL; cnt = 0; if (data != NULL) { aiov.iov_base = data; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_offset = 0; if (nbytes > INT_MAX) { error = EINVAL; goto done; } auio.uio_resid = nbytes; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; auiop = &auio; cnt = nbytes; } else { sizep = &size; } #ifdef MAC error = mac_check_vnode_listextattr(td->td_ucred, vp, attrnamespace); if (error) goto done; #endif error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep, td->td_ucred, td); if (auiop != NULL) { cnt -= auio.uio_resid; td->td_retval[0] = cnt; } else { td->td_retval[0] = size; } done: VOP_UNLOCK(vp, 0, td); return (error); } int extattr_list_fd(td, uap) struct thread *td; struct extattr_list_fd_args /* { int fd; int attrnamespace; void *data; size_t nbytes; } */ *uap; { struct file *fp; int error; error = getvnode(td->td_proc->p_fd, uap->fd, &fp); if (error) return (error); error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data, uap->nbytes, td); fdrop(fp, td); return (error); } int extattr_list_file(td, uap) struct thread*td; struct extattr_list_file_args /* { const char *path; int attrnamespace; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; int error; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } int extattr_list_link(td, uap) struct thread*td; struct extattr_list_link_args /* { const char *path; int attrnamespace; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; int error; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } Index: head/sys/kern/vfs_syscalls.c =================================================================== --- head/sys/kern/vfs_syscalls.c (revision 130343) +++ head/sys/kern/vfs_syscalls.c (revision 130344) @@ -1,4814 +1,4814 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 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. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int chroot_refuse_vdir_fds(struct filedesc *fdp); static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); static int setfown(struct thread *td, struct vnode *, uid_t, gid_t); static int setfmode(struct thread *td, struct vnode *, int); static int setfflags(struct thread *td, struct vnode *, int); static int setutimes(struct thread *td, struct vnode *, const struct timespec *, int, int); static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred, struct thread *td); static int extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, size_t nbytes, struct thread *td); int (*union_dircheckp)(struct thread *td, struct vnode **, struct file *); int (*softdep_fsync_hook)(struct vnode *); /* * The module initialization routine for POSIX asynchronous I/O will * set this to the version of AIO that it implements. (Zero means * that it is not implemented.) This value is used here by pathconf() * and in kern_descrip.c by fpathconf(). */ int async_io_version; /* * Sync each mounted filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct sync_args { int dummy; }; #endif #ifdef DEBUG static int syncprt = 0; SYSCTL_INT(_debug, OID_AUTO, syncprt, CTLFLAG_RW, &syncprt, 0, ""); #endif /* ARGSUSED */ int sync(td, uap) struct thread *td; struct sync_args *uap; { struct mount *mp, *nmp; int asyncflag; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if ((mp->mnt_flag & MNT_RDONLY) == 0 && vn_start_write(NULL, &mp, V_NOWAIT) == 0) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; vfs_msync(mp, MNT_NOWAIT); VFS_SYNC(mp, MNT_NOWAIT, ((td != NULL) ? td->td_ucred : NOCRED), td); mp->mnt_flag |= asyncflag; vn_finished_write(mp); } mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); } mtx_unlock(&mountlist_mtx); #if 0 /* * XXX don't call vfs_bufstats() yet because that routine * was not imported in the Lite2 merge. */ #ifdef DIAGNOSTIC if (syncprt) vfs_bufstats(); #endif /* DIAGNOSTIC */ #endif return (0); } /* XXX PRISON: could be per prison flag */ static int prison_quotas; #if 0 SYSCTL_INT(_kern_prison, OID_AUTO, quotas, CTLFLAG_RW, &prison_quotas, 0, ""); #endif /* * Change filesystem quotas. */ #ifndef _SYS_SYSPROTO_H_ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; #endif int quotactl(td, uap) struct thread *td; register struct quotactl_args /* { char *path; int cmd; int uid; caddr_t arg; } */ *uap; { struct mount *mp, *vmp; int error; struct nameidata nd; if (jailed(td->td_ucred) && !prison_quotas) return (EPERM); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_start_write(nd.ni_vp, &vmp, V_WAIT | PCATCH); mp = nd.ni_vp->v_mount; vrele(nd.ni_vp); if (error) return (error); error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, td); vn_finished_write(vmp); return (error); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct statfs_args { char *path; struct statfs *buf; }; #endif int statfs(td, uap) struct thread *td; register struct statfs_args /* { char *path; struct statfs *buf; } */ *uap; { struct mount *mp; struct statfs *sp, sb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif /* * Set these in case the underlying filesystem fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp, td); if (error) return (error); if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout(sp, uap->buf, sizeof(*sp))); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct fstatfs_args { int fd; struct statfs *buf; }; #endif int fstatfs(td, uap) struct thread *td; register struct fstatfs_args /* { int fd; struct statfs *buf; } */ *uap; { struct file *fp; struct mount *mp; struct statfs *sp, sb; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); mp = fp->f_vnode->v_mount; fdrop(fp, td); if (mp == NULL) return (EBADF); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif sp = &mp->mnt_stat; /* * Set these in case the underlying filesystem fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp, td); if (error) return (error); if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout(sp, uap->buf, sizeof(*sp))); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct getfsstat_args { struct statfs *buf; long bufsize; int flags; }; #endif int getfsstat(td, uap) struct thread *td; register struct getfsstat_args /* { struct statfs *buf; long bufsize; int flags; } */ *uap; { struct mount *mp, *nmp; struct statfs *sp, sb; caddr_t sfsp; long count, maxcount, error; maxcount = uap->bufsize / sizeof(struct statfs); sfsp = (caddr_t)uap->buf; count = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (!prison_check_mount(td->td_ucred, mp)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #ifdef MAC if (mac_check_mount_stat(td->td_ucred, mp) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #endif if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if (sfsp && count < maxcount) { sp = &mp->mnt_stat; /* * Set these in case the underlying filesystem * fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; /* * If MNT_NOWAIT or MNT_LAZY is specified, do not * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY * overrides MNT_WAIT. */ if (((uap->flags & (MNT_LAZY|MNT_NOWAIT)) == 0 || (uap->flags & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, td))) { mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); continue; } if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } error = copyout(sp, sfsp, sizeof(*sp)); if (error) { vfs_unbusy(mp, td); return (error); } sfsp += sizeof(*sp); } count++; mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); } mtx_unlock(&mountlist_mtx); if (sfsp && count > maxcount) td->td_retval[0] = maxcount; else td->td_retval[0] = count; return (0); } #ifdef COMPAT_FREEBSD4 /* * Get old format filesystem statistics. */ static void cvtstatfs(struct thread *, struct statfs *, struct ostatfs *); #ifndef _SYS_SYSPROTO_H_ struct freebsd4_statfs_args { char *path; struct ostatfs *buf; }; #endif int freebsd4_statfs(td, uap) struct thread *td; struct freebsd4_statfs_args /* { char *path; struct ostatfs *buf; } */ *uap; { struct mount *mp; struct statfs *sp; struct ostatfs osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif error = VFS_STATFS(mp, sp, td); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; cvtstatfs(td, sp, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_fstatfs_args { int fd; struct ostatfs *buf; }; #endif int freebsd4_fstatfs(td, uap) struct thread *td; struct freebsd4_fstatfs_args /* { int fd; struct ostatfs *buf; } */ *uap; { struct file *fp; struct mount *mp; struct statfs *sp; struct ostatfs osb; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); mp = fp->f_vnode->v_mount; fdrop(fp, td); if (mp == NULL) return (EBADF); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif sp = &mp->mnt_stat; error = VFS_STATFS(mp, sp, td); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; cvtstatfs(td, sp, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_getfsstat_args { struct ostatfs *buf; long bufsize; int flags; }; #endif int freebsd4_getfsstat(td, uap) struct thread *td; register struct freebsd4_getfsstat_args /* { struct ostatfs *buf; long bufsize; int flags; } */ *uap; { struct mount *mp, *nmp; struct statfs *sp; struct ostatfs osb; caddr_t sfsp; long count, maxcount, error; maxcount = uap->bufsize / sizeof(struct ostatfs); sfsp = (caddr_t)uap->buf; count = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (!prison_check_mount(td->td_ucred, mp)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #ifdef MAC if (mac_check_mount_stat(td->td_ucred, mp) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #endif if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if (sfsp && count < maxcount) { sp = &mp->mnt_stat; /* * If MNT_NOWAIT or MNT_LAZY is specified, do not * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY * overrides MNT_WAIT. */ if (((uap->flags & (MNT_LAZY|MNT_NOWAIT)) == 0 || (uap->flags & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, td))) { mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); continue; } sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; cvtstatfs(td, sp, &osb); error = copyout(&osb, sfsp, sizeof(osb)); if (error) { vfs_unbusy(mp, td); return (error); } sfsp += sizeof(osb); } count++; mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); } mtx_unlock(&mountlist_mtx); if (sfsp && count > maxcount) td->td_retval[0] = maxcount; else td->td_retval[0] = count; return (0); } /* * Implement fstatfs() for (NFS) file handles. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_fhstatfs_args { struct fhandle *u_fhp; struct ostatfs *buf; }; #endif int freebsd4_fhstatfs(td, uap) struct thread *td; struct freebsd4_fhstatfs_args /* { struct fhandle *u_fhp; struct ostatfs *buf; } */ *uap; { struct statfs *sp; struct mount *mp; struct vnode *vp; struct ostatfs osb; fhandle_t fh; int error; error = suser(td); if (error) return (error); if ((error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t))) != 0) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif if ((error = VFS_STATFS(mp, sp, td)) != 0) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; cvtstatfs(td, sp, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); } /* * Convert a new format statfs structure to an old format statfs structure. */ static void cvtstatfs(td, nsp, osp) struct thread *td; struct statfs *nsp; struct ostatfs *osp; { bzero(osp, sizeof(*osp)); osp->f_bsize = MIN(nsp->f_bsize, LONG_MAX); osp->f_iosize = MIN(nsp->f_iosize, LONG_MAX); osp->f_blocks = MIN(nsp->f_blocks, LONG_MAX); osp->f_bfree = MIN(nsp->f_bfree, LONG_MAX); osp->f_bavail = MIN(nsp->f_bavail, LONG_MAX); osp->f_files = MIN(nsp->f_files, LONG_MAX); osp->f_ffree = MIN(nsp->f_ffree, LONG_MAX); osp->f_owner = nsp->f_owner; osp->f_type = nsp->f_type; osp->f_flags = nsp->f_flags; osp->f_syncwrites = MIN(nsp->f_syncwrites, LONG_MAX); osp->f_asyncwrites = MIN(nsp->f_asyncwrites, LONG_MAX); osp->f_syncreads = MIN(nsp->f_syncreads, LONG_MAX); osp->f_asyncreads = MIN(nsp->f_asyncreads, LONG_MAX); bcopy(nsp->f_fstypename, osp->f_fstypename, MIN(MFSNAMELEN, OMNAMELEN)); bcopy(nsp->f_mntonname, osp->f_mntonname, MIN(MFSNAMELEN, OMNAMELEN)); bcopy(nsp->f_mntfromname, osp->f_mntfromname, MIN(MFSNAMELEN, OMNAMELEN)); if (suser(td)) { osp->f_fsid.val[0] = osp->f_fsid.val[1] = 0; } else { osp->f_fsid = nsp->f_fsid; } } #endif /* COMPAT_FREEBSD4 */ /* * Change current working directory to a given file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchdir_args { int fd; }; #endif int fchdir(td, uap) struct thread *td; struct fchdir_args /* { int fd; } */ *uap; { register struct filedesc *fdp = td->td_proc->p_fd; struct vnode *vp, *tdp, *vpold; struct mount *mp; struct file *fp; int error; if ((error = getvnode(fdp, uap->fd, &fp)) != 0) return (error); vp = fp->f_vnode; VREF(vp); fdrop(fp, td); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (vp->v_type != VDIR) error = ENOTDIR; #ifdef MAC else if ((error = mac_check_vnode_chdir(td->td_ucred, vp)) != 0) { } #endif else error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); while (!error && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, 0, 0, td)) continue; error = VFS_ROOT(mp, &tdp); vfs_unbusy(mp, td); if (error) break; vput(vp); vp = tdp; } if (error) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, td); FILEDESC_LOCK(fdp); vpold = fdp->fd_cdir; fdp->fd_cdir = vp; FILEDESC_UNLOCK(fdp); vrele(vpold); return (0); } /* * Change current working directory (``.''). */ #ifndef _SYS_SYSPROTO_H_ struct chdir_args { char *path; }; #endif int chdir(td, uap) struct thread *td; struct chdir_args /* { char *path; } */ *uap; { return (kern_chdir(td, uap->path, UIO_USERSPACE)); } int kern_chdir(struct thread *td, char *path, enum uio_seg pathseg) { register struct filedesc *fdp = td->td_proc->p_fd; int error; struct nameidata nd; struct vnode *vp; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); if ((error = change_dir(nd.ni_vp, td)) != 0) { vput(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); return (error); } VOP_UNLOCK(nd.ni_vp, 0, td); NDFREE(&nd, NDF_ONLY_PNBUF); FILEDESC_LOCK(fdp); vp = fdp->fd_cdir; fdp->fd_cdir = nd.ni_vp; FILEDESC_UNLOCK(fdp); vrele(vp); return (0); } /* * Helper function for raised chroot(2) security function: Refuse if * any filedescriptors are open directories. */ static int chroot_refuse_vdir_fds(fdp) struct filedesc *fdp; { struct vnode *vp; struct file *fp; int fd; FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); for (fd = 0; fd < fdp->fd_nfiles ; fd++) { fp = fget_locked(fdp, fd); if (fp == NULL) continue; if (fp->f_type == DTYPE_VNODE) { vp = fp->f_vnode; if (vp->v_type == VDIR) return (EPERM); } } return (0); } /* * This sysctl determines if we will allow a process to chroot(2) if it * has a directory open: * 0: disallowed for all processes. * 1: allowed for processes that were not already chroot(2)'ed. * 2: allowed for all processes. */ static int chroot_allow_open_directories = 1; SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW, &chroot_allow_open_directories, 0, ""); /* * Change notion of root (``/'') directory. */ #ifndef _SYS_SYSPROTO_H_ struct chroot_args { char *path; }; #endif int chroot(td, uap) struct thread *td; struct chroot_args /* { char *path; } */ *uap; { int error; struct nameidata nd; error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, td); mtx_lock(&Giant); error = namei(&nd); if (error) goto error; if ((error = change_dir(nd.ni_vp, td)) != 0) goto e_vunlock; #ifdef MAC if ((error = mac_check_vnode_chroot(td->td_ucred, nd.ni_vp))) goto e_vunlock; #endif VOP_UNLOCK(nd.ni_vp, 0, td); error = change_root(nd.ni_vp, td); vrele(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); mtx_unlock(&Giant); return (error); e_vunlock: vput(nd.ni_vp); error: mtx_unlock(&Giant); NDFREE(&nd, NDF_ONLY_PNBUF); return (error); } /* * Common routine for chroot and chdir. Callers must provide a locked vnode * instance. */ int change_dir(vp, td) struct vnode *vp; struct thread *td; { int error; ASSERT_VOP_LOCKED(vp, "change_dir(): vp not locked"); if (vp->v_type != VDIR) return (ENOTDIR); #ifdef MAC error = mac_check_vnode_chdir(td->td_ucred, vp); if (error) return (error); #endif error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); return (error); } /* * Common routine for kern_chroot() and jail_attach(). The caller is * responsible for invoking suser() and mac_check_chroot() to authorize this * operation. */ int change_root(vp, td) struct vnode *vp; struct thread *td; { struct filedesc *fdp; struct vnode *oldvp; int error; mtx_assert(&Giant, MA_OWNED); fdp = td->td_proc->p_fd; FILEDESC_LOCK(fdp); if (chroot_allow_open_directories == 0 || (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { error = chroot_refuse_vdir_fds(fdp); if (error) { FILEDESC_UNLOCK(fdp); return (error); } } oldvp = fdp->fd_rdir; fdp->fd_rdir = vp; VREF(fdp->fd_rdir); if (!fdp->fd_jdir) { fdp->fd_jdir = vp; VREF(fdp->fd_jdir); } FILEDESC_UNLOCK(fdp); vrele(oldvp); return (0); } /* * Check permissions, allocate an open file structure, * and call the device open routine if any. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct open_args { char *path; int flags; int mode; }; #endif int open(td, uap) struct thread *td; register struct open_args /* { char *path; int flags; int mode; } */ *uap; { return (kern_open(td, uap->path, UIO_USERSPACE, uap->flags, uap->mode)); } int kern_open(struct thread *td, char *path, enum uio_seg pathseg, int flags, int mode) { struct proc *p = td->td_proc; struct filedesc *fdp = p->p_fd; struct file *fp; struct vnode *vp; struct vattr vat; struct mount *mp; int cmode; struct file *nfp; int type, indx, error; struct flock lf; struct nameidata nd; if ((flags & O_ACCMODE) == O_ACCMODE) return (EINVAL); flags = FFLAGS(flags); error = falloc(td, &nfp, &indx); if (error) return (error); /* An extra reference on `nfp' has been held for us by falloc(). */ fp = nfp; cmode = ((mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); td->td_dupfd = -1; /* XXX check for fdopen */ mtx_lock(&Giant); error = vn_open(&nd, &flags, cmode, indx); if (error) { mtx_unlock(&Giant); /* * If the vn_open replaced the method vector, something * wonderous happened deep below and we just pass it up * pretending we know what we do. */ if (error == ENXIO && fp->f_ops != &badfileops) { fdrop(fp, td); td->td_retval[0] = indx; return (0); } /* * release our own reference */ fdrop(fp, td); /* * handle special fdopen() case. bleh. dupfdopen() is * responsible for dropping the old contents of ofiles[indx] * if it succeeds. */ if ((error == ENODEV || error == ENXIO) && td->td_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(td, fdp, indx, td->td_dupfd, flags, error)) == 0) { td->td_retval[0] = indx; return (0); } /* * Clean up the descriptor, but only if another thread hadn't * replaced or closed it. */ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdunused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else { FILEDESC_UNLOCK(fdp); } if (error == ERESTART) error = EINTR; return (error); } td->td_dupfd = 0; NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; /* * There should be 2 references on the file, one from the descriptor * table, and one for us. * * Handle the case where someone closed the file (via its file * descriptor) while we were blocked. The end result should look * like opening the file succeeded but it was immediately closed. * We call vn_close() manually because we haven't yet hooked up * the various 'struct file' fields. */ FILEDESC_LOCK(fdp); FILE_LOCK(fp); if (fp->f_count == 1) { KASSERT(fdp->fd_ofiles[indx] != fp, ("Open file descriptor lost all refs")); FILEDESC_UNLOCK(fdp); FILE_UNLOCK(fp); VOP_UNLOCK(vp, 0, td); vn_close(vp, flags & FMASK, fp->f_cred, td); mtx_unlock(&Giant); fdrop(fp, td); td->td_retval[0] = indx; return (0); } fp->f_vnode = vp; fp->f_data = vp; fp->f_flag = flags & FMASK; fp->f_ops = &vnops; fp->f_seqcount = 1; fp->f_type = (vp->v_type == VFIFO ? DTYPE_FIFO : DTYPE_VNODE); FILEDESC_UNLOCK(fdp); FILE_UNLOCK(fp); /* assert that vn_open created a backing object if one is needed */ KASSERT(!vn_canvmio(vp) || VOP_GETVOBJECT(vp, NULL) == 0, ("open: vmio vnode has no backing object after vn_open")); VOP_UNLOCK(vp, 0, td); if (flags & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (flags & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((flags & FNONBLOCK) == 0) type |= F_WAIT; if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) goto bad; fp->f_flag |= FHASLOCK; } if (flags & O_TRUNC) { if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) goto bad; VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); VATTR_NULL(&vat); vat.va_size = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); #ifdef MAC error = mac_check_vnode_write(td->td_ucred, fp->f_cred, vp); if (error == 0) #endif error = VOP_SETATTR(vp, &vat, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); if (error) goto bad; } mtx_unlock(&Giant); /* * Release our private reference, leaving the one associated with * the descriptor table intact. */ fdrop(fp, td); td->td_retval[0] = indx; return (0); bad: mtx_unlock(&Giant); FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdunused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else { FILEDESC_UNLOCK(fdp); } fdrop(fp, td); return (error); } #ifdef COMPAT_43 /* * Create a file. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct ocreat_args { char *path; int mode; }; #endif int ocreat(td, uap) struct thread *td; register struct ocreat_args /* { char *path; int mode; } */ *uap; { return (kern_open(td, uap->path, UIO_USERSPACE, O_WRONLY | O_CREAT | O_TRUNC, uap->mode)); } #endif /* COMPAT_43 */ /* * Create a special file. */ #ifndef _SYS_SYSPROTO_H_ struct mknod_args { char *path; int mode; int dev; }; #endif int mknod(td, uap) struct thread *td; register struct mknod_args /* { char *path; int mode; int dev; } */ *uap; { return (kern_mknod(td, uap->path, UIO_USERSPACE, uap->mode, uap->dev)); } int kern_mknod(struct thread *td, char *path, enum uio_seg pathseg, int mode, int dev) { struct vnode *vp; struct mount *mp; struct vattr vattr; int error; int whiteout = 0; struct nameidata nd; switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: error = suser(td); break; default: error = suser_cred(td->td_ucred, PRISON_ROOT); break; } if (error) return (error); restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); if (vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (EEXIST); } else { VATTR_NULL(&vattr); FILEDESC_LOCK(td->td_proc->p_fd); vattr.va_mode = (mode & ALLPERMS) & ~td->td_proc->p_fd->fd_cmask; FILEDESC_UNLOCK(td->td_proc->p_fd); vattr.va_rdev = dev; whiteout = 0; switch (mode & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ vattr.va_type = VBAD; break; case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; case S_IFWHT: whiteout = 1; break; default: error = EINVAL; break; } } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } #ifdef MAC if (error == 0 && !whiteout) error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); #endif if (!error) { VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); if (whiteout) error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); else { error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); } } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mknod"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mknod"); return (error); } /* * Create a named pipe. */ #ifndef _SYS_SYSPROTO_H_ struct mkfifo_args { char *path; int mode; }; #endif int mkfifo(td, uap) struct thread *td; register struct mkfifo_args /* { char *path; int mode; } */ *uap; { return (kern_mkfifo(td, uap->path, UIO_USERSPACE, uap->mode)); } int kern_mkfifo(struct thread *td, char *path, enum uio_seg pathseg, int mode) { struct mount *mp; struct vattr vattr; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VFIFO; FILEDESC_LOCK(td->td_proc->p_fd); vattr.va_mode = (mode & ALLPERMS) & ~td->td_proc->p_fd->fd_cmask; FILEDESC_UNLOCK(td->td_proc->p_fd); #ifdef MAC error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error) goto out; #endif VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); #ifdef MAC out: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); return (error); } /* * Make a hard file link. */ #ifndef _SYS_SYSPROTO_H_ struct link_args { char *path; char *link; }; #endif int link(td, uap) struct thread *td; register struct link_args /* { char *path; char *link; } */ *uap; { return (kern_link(td, uap->path, uap->link, UIO_USERSPACE)); } SYSCTL_DECL(_security_bsd); static int hardlink_check_uid = 0; SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, &hardlink_check_uid, 0, "Unprivileged processes cannot create hard links to files owned by other " "users"); static int hardlink_check_gid = 0; SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_gid, CTLFLAG_RW, &hardlink_check_gid, 0, "Unprivileged processes cannot create hard links to files owned by other " "groups"); static int can_hardlink(struct vnode *vp, struct thread *td, struct ucred *cred) { struct vattr va; int error; if (suser_cred(cred, PRISON_ROOT) == 0) return (0); if (!hardlink_check_uid && !hardlink_check_gid) return (0); error = VOP_GETATTR(vp, &va, cred, td); if (error != 0) return (error); if (hardlink_check_uid) { if (cred->cr_uid != va.va_uid) return (EPERM); } if (hardlink_check_gid) { if (!groupmember(va.va_gid, cred)) return (EPERM); } return (0); } int kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg) { struct vnode *vp; struct mount *mp; struct nameidata nd; int error; bwillwrite(); NDINIT(&nd, LOOKUP, FOLLOW|NOOBJ, segflg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type == VDIR) { vrele(vp); return (EPERM); /* POSIX */ } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } NDINIT(&nd, CREATE, LOCKPARENT | NOOBJ | SAVENAME, segflg, link, td); if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) { vrele(nd.ni_vp); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); error = EEXIST; } else if ((error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td)) == 0) { VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); error = can_hardlink(vp, td, td->td_ucred); if (error == 0) #ifdef MAC error = mac_check_vnode_link(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); if (error == 0) #endif error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); VOP_UNLOCK(vp, 0, td); vput(nd.ni_dvp); } NDFREE(&nd, NDF_ONLY_PNBUF); } vrele(vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "link"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "link"); return (error); } /* * Make a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct symlink_args { char *path; char *link; }; #endif int symlink(td, uap) struct thread *td; register struct symlink_args /* { char *path; char *link; } */ *uap; { return (kern_symlink(td, uap->path, uap->link, UIO_USERSPACE)); } int kern_symlink(struct thread *td, char *path, char *link, enum uio_seg segflg) { struct mount *mp; struct vattr vattr; char *syspath; int error; struct nameidata nd; if (segflg == UIO_SYSSPACE) { syspath = path; } else { syspath = uma_zalloc(namei_zone, M_WAITOK); if ((error = copyinstr(path, syspath, MAXPATHLEN, NULL)) != 0) goto out; } restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT | NOOBJ | SAVENAME, segflg, link, td); if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); error = EEXIST; goto out; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) goto out; goto restart; } VATTR_NULL(&vattr); FILEDESC_LOCK(td->td_proc->p_fd); vattr.va_mode = ACCESSPERMS &~ td->td_proc->p_fd->fd_cmask; FILEDESC_UNLOCK(td->td_proc->p_fd); #ifdef MAC vattr.va_type = VLNK; error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error) goto out2; #endif VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, syspath); if (error == 0) vput(nd.ni_vp); #ifdef MAC out2: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "symlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "symlink"); out: if (segflg != UIO_SYSSPACE) uma_zfree(namei_zone, syspath); return (error); } /* * Delete a whiteout from the filesystem. */ int undelete(td, uap) struct thread *td; register struct undelete_args /* { char *path; } */ *uap; { int error; struct mount *mp; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp) vrele(nd.ni_vp); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "undelete"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "undelete"); return (error); } /* * Delete a name from the filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct unlink_args { char *path; }; #endif int unlink(td, uap) struct thread *td; struct unlink_args /* { char *path; } */ *uap; { return (kern_unlink(td, uap->path, UIO_USERSPACE)); } int kern_unlink(struct thread *td, char *path, enum uio_seg pathseg) { struct mount *mp; struct vnode *vp; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT|LOCKLEAF, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { /* * The root of a mounted filesystem cannot be deleted. * * XXX: can this only be a VDIR case? */ if (vp->v_vflag & VV_ROOT) error = EBUSY; } if (error == 0) { if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); if (vp == nd.ni_dvp) vrele(vp); else vput(vp); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } #ifdef MAC error = mac_check_vnode_delete(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); if (error) goto out; #endif VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); #ifdef MAC out: #endif vn_finished_write(mp); } NDFREE(&nd, NDF_ONLY_PNBUF); if (vp == nd.ni_dvp) vrele(vp); else vput(vp); vput(nd.ni_dvp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "unlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "unlink"); return (error); } /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct lseek_args { int fd; int pad; off_t offset; int whence; }; #endif int lseek(td, uap) struct thread *td; register struct lseek_args /* { int fd; int pad; off_t offset; int whence; } */ *uap; { struct ucred *cred = td->td_ucred; struct file *fp; struct vnode *vp; struct vattr vattr; off_t offset; int error, noneg; if ((error = fget(td, uap->fd, &fp)) != 0) return (error); if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) { fdrop(fp, td); return (ESPIPE); } vp = fp->f_vnode; noneg = (vp->v_type != VCHR); offset = uap->offset; switch (uap->whence) { case L_INCR: if (noneg && (fp->f_offset < 0 || (offset > 0 && fp->f_offset > OFF_MAX - offset))) { error = EOVERFLOW; break; } offset += fp->f_offset; break; case L_XTND: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = VOP_GETATTR(vp, &vattr, cred, td); VOP_UNLOCK(vp, 0, td); if (error) break; if (noneg && (vattr.va_size > OFF_MAX || (offset > 0 && vattr.va_size > OFF_MAX - offset))) { error = EOVERFLOW; break; } offset += vattr.va_size; break; case L_SET: break; default: error = EINVAL; } if (error == 0 && noneg && offset < 0) error = EINVAL; if (error != 0) { fdrop(fp, td); return (error); } fp->f_offset = offset; *(off_t *)(td->td_retval) = fp->f_offset; fdrop(fp, td); return (0); } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct olseek_args { int fd; long offset; int whence; }; #endif int olseek(td, uap) struct thread *td; register struct olseek_args /* { int fd; long offset; int whence; } */ *uap; { struct lseek_args /* { int fd; int pad; off_t offset; int whence; } */ nuap; int error; nuap.fd = uap->fd; nuap.offset = uap->offset; nuap.whence = uap->whence; error = lseek(td, &nuap); return (error); } #endif /* COMPAT_43 */ /* * Check access permissions using passed credentials. */ static int vn_access(vp, user_flags, cred, td) struct vnode *vp; int user_flags; struct ucred *cred; struct thread *td; { int error, flags; /* Flags == 0 means only check for existence. */ error = 0; if (user_flags) { flags = 0; if (user_flags & R_OK) flags |= VREAD; if (user_flags & W_OK) flags |= VWRITE; if (user_flags & X_OK) flags |= VEXEC; #ifdef MAC error = mac_check_vnode_access(cred, vp, flags); if (error) return (error); #endif if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, flags, cred, td); } return (error); } /* * Check access permissions using "real" credentials. */ #ifndef _SYS_SYSPROTO_H_ struct access_args { char *path; int flags; }; #endif int access(td, uap) struct thread *td; register struct access_args /* { char *path; int flags; } */ *uap; { return (kern_access(td, uap->path, UIO_USERSPACE, uap->flags)); } int kern_access(struct thread *td, char *path, enum uio_seg pathseg, int flags) { struct ucred *cred, *tmpcred; register struct vnode *vp; int error; struct nameidata nd; /* * Create and modify a temporary credential instead of one that * is potentially shared. This could also mess up socket * buffer accounting which can run in an interrupt context. */ cred = td->td_ucred; tmpcred = crdup(cred); tmpcred->cr_uid = cred->cr_ruid; tmpcred->cr_groups[0] = cred->cr_rgid; td->td_ucred = tmpcred; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, pathseg, path, td); if ((error = namei(&nd)) != 0) goto out1; vp = nd.ni_vp; error = vn_access(vp, flags, tmpcred, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); out1: td->td_ucred = cred; crfree(tmpcred); return (error); } /* * Check access permissions using "effective" credentials. */ #ifndef _SYS_SYSPROTO_H_ struct eaccess_args { char *path; int flags; }; #endif int eaccess(td, uap) struct thread *td; register struct eaccess_args /* { char *path; int flags; } */ *uap; { struct nameidata nd; struct vnode *vp; int error; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_access(vp, uap->flags, td->td_ucred, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); return (error); } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct ostat_args { char *path; struct ostat *ub; }; #endif int ostat(td, uap) struct thread *td; register struct ostat_args /* { char *path; struct ostat *ub; } */ *uap; { struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); vput(nd.ni_vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout(&osb, uap->ub, sizeof (osb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct olstat_args { char *path; struct ostat *ub; }; #endif int olstat(td, uap) struct thread *td; register struct olstat_args /* { char *path; struct ostat *ub; } */ *uap; { struct vnode *vp; struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout(&osb, uap->ub, sizeof (osb)); return (error); } /* * Convert from an old to a new stat structure. */ void cvtstat(st, ost) struct stat *st; struct ostat *ost; { ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (st->st_size < (quad_t)1 << 32) ost->st_size = st->st_size; else ost->st_size = -2; ost->st_atime = st->st_atime; ost->st_mtime = st->st_mtime; ost->st_ctime = st->st_ctime; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct stat_args { char *path; struct stat *ub; }; #endif int stat(td, uap) struct thread *td; register struct stat_args /* { char *path; struct stat *ub; } */ *uap; { struct stat sb; int error; struct nameidata nd; #ifdef LOOKUP_SHARED NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); #else NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); #endif if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); if (error) return (error); error = copyout(&sb, uap->ub, sizeof (sb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif int lstat(td, uap) struct thread *td; register struct lstat_args /* { char *path; struct stat *ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); error = copyout(&sb, uap->ub, sizeof (sb)); return (error); } /* * Implementation of the NetBSD stat() function. * XXX This should probably be collapsed with the FreeBSD version, * as the differences are only due to vn_stat() clearing spares at * the end of the structures. vn_stat could be split to avoid this, * and thus collapse the following to close to zero code. */ void cvtnstat(sb, nsb) struct stat *sb; struct nstat *nsb; { bzero(nsb, sizeof *nsb); nsb->st_dev = sb->st_dev; nsb->st_ino = sb->st_ino; nsb->st_mode = sb->st_mode; nsb->st_nlink = sb->st_nlink; nsb->st_uid = sb->st_uid; nsb->st_gid = sb->st_gid; nsb->st_rdev = sb->st_rdev; nsb->st_atimespec = sb->st_atimespec; nsb->st_mtimespec = sb->st_mtimespec; nsb->st_ctimespec = sb->st_ctimespec; nsb->st_size = sb->st_size; nsb->st_blocks = sb->st_blocks; nsb->st_blksize = sb->st_blksize; nsb->st_flags = sb->st_flags; nsb->st_gen = sb->st_gen; nsb->st_birthtimespec = sb->st_birthtimespec; } #ifndef _SYS_SYSPROTO_H_ struct nstat_args { char *path; struct nstat *ub; }; #endif int nstat(td, uap) struct thread *td; register struct nstat_args /* { char *path; struct nstat *ub; } */ *uap; { struct stat sb; struct nstat nsb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); vput(nd.ni_vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout(&nsb, uap->ub, sizeof (nsb)); return (error); } /* * NetBSD lstat. Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif int nlstat(td, uap) struct thread *td; register struct nlstat_args /* { char *path; struct nstat *ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nstat nsb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); vput(vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout(&nsb, uap->ub, sizeof (nsb)); return (error); } /* * Get configurable pathname variables. */ #ifndef _SYS_SYSPROTO_H_ struct pathconf_args { char *path; int name; }; #endif int pathconf(td, uap) struct thread *td; register struct pathconf_args /* { char *path; int name; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); /* If asynchronous I/O is available, it works for all files. */ if (uap->name == _PC_ASYNC_IO) td->td_retval[0] = async_io_version; else error = VOP_PATHCONF(nd.ni_vp, uap->name, td->td_retval); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct readlink_args { char *path; char *buf; int count; }; #endif int readlink(td, uap) struct thread *td; register struct readlink_args /* { char *path; char *buf; int count; } */ *uap; { return (kern_readlink(td, uap->path, UIO_USERSPACE, uap->buf, UIO_USERSPACE, uap->count)); } int kern_readlink(struct thread *td, char *path, enum uio_seg pathseg, char *buf, enum uio_seg bufseg, int count) { register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; #ifdef MAC error = mac_check_vnode_readlink(td->td_ucred, vp); if (error) { vput(vp); return (error); } #endif if (vp->v_type != VLNK) error = EINVAL; else { aiov.iov_base = buf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = bufseg; auio.uio_td = td; auio.uio_resid = count; error = VOP_READLINK(vp, &auio, td->td_ucred); } vput(vp); td->td_retval[0] = count - auio.uio_resid; return (error); } /* * Common implementation code for chflags() and fchflags(). */ static int setfflags(td, vp, flags) struct thread *td; struct vnode *vp; int flags; { int error; struct mount *mp; struct vattr vattr; /* * Prevent non-root users from setting flags on devices. When * a device is reused, users can retain ownership of the device * if they are allowed to set flags and programs assume that * chown can't fail when done as root. */ if (vp->v_type == VCHR || vp->v_type == VBLK) { error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VATTR_NULL(&vattr); vattr.va_flags = flags; #ifdef MAC error = mac_check_vnode_setflags(td->td_ucred, vp, vattr.va_flags); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } /* * Change flags of a file given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chflags_args { char *path; int flags; }; #endif int chflags(td, uap) struct thread *td; register struct chflags_args /* { char *path; int flags; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(td, nd.ni_vp, uap->flags); vrele(nd.ni_vp); return (error); } /* * Same as chflags() but doesn't follow symlinks. */ int lchflags(td, uap) struct thread *td; register struct lchflags_args /* { char *path; int flags; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(td, nd.ni_vp, uap->flags); vrele(nd.ni_vp); return (error); } /* * Change flags of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchflags_args { int fd; int flags; }; #endif int fchflags(td, uap) struct thread *td; register struct fchflags_args /* { int fd; int flags; } */ *uap; { struct file *fp; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); error = setfflags(td, fp->f_vnode, uap->flags); fdrop(fp, td); return (error); } /* * Common implementation code for chmod(), lchmod() and fchmod(). */ static int setfmode(td, vp, mode) struct thread *td; struct vnode *vp; int mode; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VATTR_NULL(&vattr); vattr.va_mode = mode & ALLPERMS; #ifdef MAC error = mac_check_vnode_setmode(td->td_ucred, vp, vattr.va_mode); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } /* * Change mode of a file given path name. */ #ifndef _SYS_SYSPROTO_H_ struct chmod_args { char *path; int mode; }; #endif int chmod(td, uap) struct thread *td; register struct chmod_args /* { char *path; int mode; } */ *uap; { return (kern_chmod(td, uap->path, UIO_USERSPACE, uap->mode)); } int kern_chmod(struct thread *td, char *path, enum uio_seg pathseg, int mode) { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(td, nd.ni_vp, mode); vrele(nd.ni_vp); return (error); } /* * Change mode of a file given path name (don't follow links.) */ #ifndef _SYS_SYSPROTO_H_ struct lchmod_args { char *path; int mode; }; #endif int lchmod(td, uap) struct thread *td; register struct lchmod_args /* { char *path; int mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(td, nd.ni_vp, uap->mode); vrele(nd.ni_vp); return (error); } /* * Change mode of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchmod_args { int fd; int mode; }; #endif int fchmod(td, uap) struct thread *td; register struct fchmod_args /* { int fd; int mode; } */ *uap; { struct file *fp; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); error = setfmode(td, fp->f_vnode, uap->mode); fdrop(fp, td); return (error); } /* * Common implementation for chown(), lchown(), and fchown() */ static int setfown(td, vp, uid, gid) struct thread *td; struct vnode *vp; uid_t uid; gid_t gid; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VATTR_NULL(&vattr); vattr.va_uid = uid; vattr.va_gid = gid; #ifdef MAC error = mac_check_vnode_setowner(td->td_ucred, vp, vattr.va_uid, vattr.va_gid); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } /* * Set ownership given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chown_args { char *path; int uid; int gid; }; #endif int chown(td, uap) struct thread *td; register struct chown_args /* { char *path; int uid; int gid; } */ *uap; { return (kern_chown(td, uap->path, UIO_USERSPACE, uap->uid, uap->gid)); } int kern_chown(struct thread *td, char *path, enum uio_seg pathseg, int uid, int gid) { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(td, nd.ni_vp, uid, gid); vrele(nd.ni_vp); return (error); } /* * Set ownership given a path name, do not cross symlinks. */ #ifndef _SYS_SYSPROTO_H_ struct lchown_args { char *path; int uid; int gid; }; #endif int lchown(td, uap) struct thread *td; register struct lchown_args /* { char *path; int uid; int gid; } */ *uap; { return (kern_lchown(td, uap->path, UIO_USERSPACE, uap->uid, uap->gid)); } int kern_lchown(struct thread *td, char *path, enum uio_seg pathseg, int uid, int gid) { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(td, nd.ni_vp, uid, gid); vrele(nd.ni_vp); return (error); } /* * Set ownership given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchown_args { int fd; int uid; int gid; }; #endif int fchown(td, uap) struct thread *td; register struct fchown_args /* { int fd; int uid; int gid; } */ *uap; { struct file *fp; int error; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); error = setfown(td, fp->f_vnode, uap->uid, uap->gid); fdrop(fp, td); return (error); } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int getutimes(usrtvp, tvpseg, tsp) const struct timeval *usrtvp; enum uio_seg tvpseg; struct timespec *tsp; { struct timeval tv[2]; const struct timeval *tvp; int error; if (usrtvp == NULL) { microtime(&tv[0]); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); tsp[1] = tsp[0]; } else { if (tvpseg == UIO_SYSSPACE) { tvp = usrtvp; } else { if ((error = copyin(usrtvp, tv, sizeof(tv))) != 0) return (error); tvp = tv; } TIMEVAL_TO_TIMESPEC(&tvp[0], &tsp[0]); TIMEVAL_TO_TIMESPEC(&tvp[1], &tsp[1]); } return (0); } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int setutimes(td, vp, ts, numtimes, nullflag) struct thread *td; struct vnode *vp; const struct timespec *ts; int numtimes; int nullflag; { int error, setbirthtime; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); setbirthtime = 0; if (numtimes < 3 && VOP_GETATTR(vp, &vattr, td->td_ucred, td) == 0 && timespeccmp(&ts[1], &vattr.va_birthtime, < )) setbirthtime = 1; VATTR_NULL(&vattr); vattr.va_atime = ts[0]; vattr.va_mtime = ts[1]; if (setbirthtime) vattr.va_birthtime = ts[1]; if (numtimes > 2) vattr.va_birthtime = ts[2]; if (nullflag) vattr.va_vaflags |= VA_UTIMES_NULL; #ifdef MAC error = mac_check_vnode_setutimes(td->td_ucred, vp, vattr.va_atime, vattr.va_mtime); #endif if (error == 0) error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct utimes_args { char *path; struct timeval *tptr; }; #endif int utimes(td, uap) struct thread *td; register struct utimes_args /* { char *path; struct timeval *tptr; } */ *uap; { return (kern_utimes(td, uap->path, UIO_USERSPACE, uap->tptr, UIO_USERSPACE)); } int kern_utimes(struct thread *td, char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; int error; struct nameidata nd; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(td, nd.ni_vp, ts, 2, tptr == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct lutimes_args { char *path; struct timeval *tptr; }; #endif int lutimes(td, uap) struct thread *td; register struct lutimes_args /* { char *path; struct timeval *tptr; } */ *uap; { return (kern_lutimes(td, uap->path, UIO_USERSPACE, uap->tptr, UIO_USERSPACE)); } int kern_lutimes(struct thread *td, char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; int error; struct nameidata nd; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(td, nd.ni_vp, ts, 2, tptr == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct futimes_args { int fd; struct timeval *tptr; }; #endif int futimes(td, uap) struct thread *td; register struct futimes_args /* { int fd; struct timeval *tptr; } */ *uap; { return (kern_futimes(td, uap->fd, uap->tptr, UIO_USERSPACE)); } int kern_futimes(struct thread *td, int fd, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; struct file *fp; int error; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); if ((error = getvnode(td->td_proc->p_fd, fd, &fp)) != 0) return (error); error = setutimes(td, fp->f_vnode, ts, 2, tptr == NULL); fdrop(fp, td); return (error); } /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct truncate_args { char *path; int pad; off_t length; }; #endif int truncate(td, uap) struct thread *td; register struct truncate_args /* { char *path; int pad; off_t length; } */ *uap; { return (kern_truncate(td, uap->path, UIO_USERSPACE, uap->length)); } int kern_truncate(struct thread *td, char *path, enum uio_seg pathseg, off_t length) { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (vp->v_type == VDIR) error = EISDIR; #ifdef MAC else if ((error = mac_check_vnode_write(td->td_ucred, NOCRED, vp))) { } #endif else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td)) == 0) { VATTR_NULL(&vattr); vattr.va_size = length; error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); } vput(vp); vn_finished_write(mp); return (error); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct ftruncate_args { int fd; int pad; off_t length; }; #endif int ftruncate(td, uap) struct thread *td; register struct ftruncate_args /* { int fd; int pad; off_t length; } */ *uap; { struct mount *mp; struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (uap->length < 0) return(EINVAL); if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); if ((fp->f_flag & FWRITE) == 0) { fdrop(fp, td); return (EINVAL); } vp = fp->f_vnode; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { fdrop(fp, td); return (error); } VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (vp->v_type == VDIR) error = EISDIR; #ifdef MAC else if ((error = mac_check_vnode_write(td->td_ucred, fp->f_cred, vp))) { } #endif else if ((error = vn_writechk(vp)) == 0) { VATTR_NULL(&vattr); vattr.va_size = uap->length; error = VOP_SETATTR(vp, &vattr, fp->f_cred, td); } VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); fdrop(fp, td); return (error); } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct otruncate_args { char *path; long length; }; #endif int otruncate(td, uap) struct thread *td; register struct otruncate_args /* { char *path; long length; } */ *uap; { struct truncate_args /* { char *path; int pad; off_t length; } */ nuap; nuap.path = uap->path; nuap.length = uap->length; return (truncate(td, &nuap)); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct oftruncate_args { int fd; long length; }; #endif int oftruncate(td, uap) struct thread *td; register struct oftruncate_args /* { int fd; long length; } */ *uap; { struct ftruncate_args /* { int fd; int pad; off_t length; } */ nuap; nuap.fd = uap->fd; nuap.length = uap->length; return (ftruncate(td, &nuap)); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ /* * Sync an open file. */ #ifndef _SYS_SYSPROTO_H_ struct fsync_args { int fd; }; #endif int fsync(td, uap) struct thread *td; struct fsync_args /* { int fd; } */ *uap; { struct vnode *vp; struct mount *mp; struct file *fp; vm_object_t obj; int error; GIANT_REQUIRED; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); vp = fp->f_vnode; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { fdrop(fp, td); return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (VOP_GETVOBJECT(vp, &obj) == 0) { VM_OBJECT_LOCK(obj); vm_object_page_clean(obj, 0, 0, 0); VM_OBJECT_UNLOCK(obj); } error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, td); if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP) && softdep_fsync_hook != NULL) error = (*softdep_fsync_hook)(vp); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); fdrop(fp, td); return (error); } /* * Rename files. Source and destination must either both be directories, * or both not be directories. If target is a directory, it must be empty. */ #ifndef _SYS_SYSPROTO_H_ struct rename_args { char *from; char *to; }; #endif int rename(td, uap) struct thread *td; register struct rename_args /* { char *from; char *to; } */ *uap; { return (kern_rename(td, uap->from, uap->to, UIO_USERSPACE)); } int kern_rename(struct thread *td, char *from, char *to, enum uio_seg pathseg) { struct mount *mp = NULL; struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; bwillwrite(); #ifdef MAC NDINIT(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART, pathseg, from, td); #else NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, pathseg, from, td); #endif if ((error = namei(&fromnd)) != 0) return (error); #ifdef MAC error = mac_check_vnode_rename_from(td->td_ucred, fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd); VOP_UNLOCK(fromnd.ni_dvp, 0, td); VOP_UNLOCK(fromnd.ni_vp, 0, td); #endif fvp = fromnd.ni_vp; if (error == 0) error = vn_start_write(fvp, &mp, V_WAIT | PCATCH); if (error != 0) { NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | NOOBJ, pathseg, to, td); if (fromnd.ni_vp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&tond)) != 0) { /* Translate error code for rename("dir1", "dir2/."). */ if (error == EISDIR && fvp->v_type == VDIR) error = EINVAL; NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } if (fvp == tdvp) error = EINVAL; /* * If the source is the same as the destination (that is, if they * are links to the same vnode), then there is nothing to do. */ if (fvp == tvp) error = -1; #ifdef MAC else error = mac_check_vnode_rename_to(td->td_ucred, tdvp, tond.ni_vp, fromnd.ni_dvp == tdvp, &tond.ni_cnd); #endif out: if (!error) { VOP_LEASE(tdvp, td, td->td_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) { VOP_LEASE(fromnd.ni_dvp, td, td->td_ucred, LEASE_WRITE); } if (tvp) { VOP_LEASE(tvp, td, td->td_ucred, LEASE_WRITE); } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); } else { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); ASSERT_VOP_UNLOCKED(fromnd.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(fromnd.ni_vp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_vp, "rename"); out1: vn_finished_write(mp); if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); if (error == -1) return (0); return (error); } /* * Make a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct mkdir_args { char *path; int mode; }; #endif int mkdir(td, uap) struct thread *td; register struct mkdir_args /* { char *path; int mode; } */ *uap; { return (kern_mkdir(td, uap->path, UIO_USERSPACE, uap->mode)); } int kern_mkdir(struct thread *td, char *path, enum uio_seg segflg, int mode) { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME, segflg, path, td); nd.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); /* * XXX namei called with LOCKPARENT but not LOCKLEAF has * the strange behaviour of leaving the vnode unlocked * if the target is the same vnode as the parent. */ if (vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VDIR; FILEDESC_LOCK(td->td_proc->p_fd); vattr.va_mode = (mode & ACCESSPERMS) &~ td->td_proc->p_fd->fd_cmask; FILEDESC_UNLOCK(td->td_proc->p_fd); #ifdef MAC error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error) goto out; #endif VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); #ifdef MAC out: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (!error) vput(nd.ni_vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mkdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mkdir"); return (error); } /* * Remove a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct rmdir_args { char *path; }; #endif int rmdir(td, uap) struct thread *td; struct rmdir_args /* { char *path; } */ *uap; { return (kern_rmdir(td, uap->path, UIO_USERSPACE)); } int kern_rmdir(struct thread *td, char *path, enum uio_seg pathseg) { struct mount *mp; struct vnode *vp; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_vflag & VV_ROOT) { error = EBUSY; goto out; } #ifdef MAC error = mac_check_vnode_delete(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); if (error) goto out; #endif if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); vn_finished_write(mp); out: NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "rmdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "rmdir"); return (error); } #ifdef COMPAT_43 /* * Read a block of directory entries in a filesystem independent format. */ #ifndef _SYS_SYSPROTO_H_ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int ogetdirentries(td, uap) struct thread *td; register struct ogetdirentries_args /* { int fd; char *buf; u_int count; long *basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; int error, eofflag, readcnt; long loff; /* XXX arbitrary sanity limit on `count'. */ if (uap->count > 64 * 1024) return (EINVAL); if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); return (EBADF); } vp = fp->f_vnode; unionread: if (vp->v_type != VDIR) { fdrop(fp, td); return (EINVAL); } aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; auio.uio_resid = uap->count; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); loff = auio.uio_offset = fp->f_offset; #ifdef MAC error = mac_check_vnode_readdir(td->td_ucred, vp); if (error) { VOP_UNLOCK(vp, 0, td); fdrop(fp, td); return (error); } #endif # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kuio.uio_segflg = UIO_SYSSPACE; kiov.iov_len = uap->count; MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = uap->count - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } FREE(dirbuf, M_TEMP); } VOP_UNLOCK(vp, 0, td); if (error) { fdrop(fp, td); return (error); } if (uap->count == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(td, &vp, fp); if (error == -1) goto unionread; if (error) { fdrop(fp, td); return (error); } } /* * XXX We could delay dropping the lock above but * union_dircheckp complicates things. */ vn_lock(vp, LK_EXCLUSIVE|LK_RETRY, td); if ((vp->v_vflag & VV_ROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_vnode = vp; fp->f_data = vp; fp->f_offset = 0; vput(tvp); goto unionread; } VOP_UNLOCK(vp, 0, td); } error = copyout(&loff, uap->basep, sizeof(long)); fdrop(fp, td); td->td_retval[0] = uap->count - auio.uio_resid; return (error); } #endif /* COMPAT_43 */ /* * Read a block of directory entries in a filesystem independent format. */ #ifndef _SYS_SYSPROTO_H_ struct getdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int getdirentries(td, uap) struct thread *td; register struct getdirentries_args /* { int fd; char *buf; u_int count; long *basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error, eofflag; if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); return (EBADF); } vp = fp->f_vnode; unionread: if (vp->v_type != VDIR) { fdrop(fp, td); return (EINVAL); } aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; auio.uio_resid = uap->count; /* vn_lock(vp, LK_SHARED | LK_RETRY, td); */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); loff = auio.uio_offset = fp->f_offset; #ifdef MAC error = mac_check_vnode_readdir(td->td_ucred, vp); if (error == 0) #endif error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp, 0, td); if (error) { fdrop(fp, td); return (error); } if (uap->count == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(td, &vp, fp); if (error == -1) goto unionread; if (error) { fdrop(fp, td); return (error); } } /* * XXX We could delay dropping the lock above but * union_dircheckp complicates things. */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if ((vp->v_vflag & VV_ROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_vnode = vp; fp->f_data = vp; fp->f_offset = 0; vput(tvp); goto unionread; } VOP_UNLOCK(vp, 0, td); } if (uap->basep != NULL) { error = copyout(&loff, uap->basep, sizeof(long)); } td->td_retval[0] = uap->count - auio.uio_resid; fdrop(fp, td); return (error); } #ifndef _SYS_SYSPROTO_H_ struct getdents_args { int fd; char *buf; size_t count; }; #endif int getdents(td, uap) struct thread *td; register struct getdents_args /* { int fd; char *buf; u_int count; } */ *uap; { struct getdirentries_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; ap.basep = NULL; return (getdirentries(td, &ap)); } /* * Set the mode mask for creation of filesystem nodes. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct umask_args { int newmask; }; #endif int umask(td, uap) struct thread *td; struct umask_args /* { int newmask; } */ *uap; { register struct filedesc *fdp; FILEDESC_LOCK(td->td_proc->p_fd); fdp = td->td_proc->p_fd; td->td_retval[0] = fdp->fd_cmask; fdp->fd_cmask = uap->newmask & ALLPERMS; FILEDESC_UNLOCK(td->td_proc->p_fd); return (0); } /* * Void all references to file by ripping underlying filesystem * away from vnode. */ #ifndef _SYS_SYSPROTO_H_ struct revoke_args { char *path; }; #endif int revoke(td, uap) struct thread *td; register struct revoke_args /* { char *path; } */ *uap; { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VCHR) { vput(vp); return (EINVAL); } #ifdef MAC error = mac_check_vnode_revoke(td->td_ucred, vp); if (error) { vput(vp); return (error); } #endif error = VOP_GETATTR(vp, &vattr, td->td_ucred, td); if (error) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, td); if (td->td_ucred->cr_uid != vattr.va_uid) { error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) goto out; } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) goto out; if (vcount(vp) > 1) VOP_REVOKE(vp, REVOKEALL); vn_finished_write(mp); out: vrele(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry. * A reference on the file entry is held upon returning. */ int getvnode(fdp, fd, fpp) struct filedesc *fdp; int fd; struct file **fpp; { int error; struct file *fp; fp = NULL; if (fdp == NULL) error = EBADF; else { FILEDESC_LOCK(fdp); if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) error = EBADF; else if (fp->f_vnode == NULL) { fp = NULL; error = EINVAL; } else { fhold(fp); error = 0; } FILEDESC_UNLOCK(fdp); } *fpp = fp; return (error); } /* * Get (NFS) file handle */ #ifndef _SYS_SYSPROTO_H_ struct lgetfh_args { char *fname; fhandle_t *fhp; }; #endif int lgetfh(td, uap) struct thread *td; register struct lgetfh_args *uap; { struct nameidata nd; fhandle_t fh; register struct vnode *vp; int error; error = suser(td); if (error) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error) return (error); error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } #ifndef _SYS_SYSPROTO_H_ struct getfh_args { char *fname; fhandle_t *fhp; }; #endif int getfh(td, uap) struct thread *td; register struct getfh_args *uap; { struct nameidata nd; fhandle_t fh; register struct vnode *vp; int error; error = suser(td); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error) return (error); error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } /* * syscall for the rpc.lockd to use to translate a NFS file handle into * an open descriptor. * * warning: do not remove the suser() call or this becomes one giant * security hole. */ #ifndef _SYS_SYSPROTO_H_ struct fhopen_args { const struct fhandle *u_fhp; int flags; }; #endif int fhopen(td, uap) struct thread *td; struct fhopen_args /* { const struct fhandle *u_fhp; int flags; } */ *uap; { struct proc *p = td->td_proc; struct mount *mp; struct vnode *vp; struct fhandle fhp; struct vattr vat; struct vattr *vap = &vat; struct flock lf; struct file *fp; register struct filedesc *fdp = p->p_fd; int fmode, mode, error, type; struct file *nfp; int indx; error = suser(td); if (error) return (error); fmode = FFLAGS(uap->flags); /* why not allow a non-read/write open for our lockd? */ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); error = copyin(uap->u_fhp, &fhp, sizeof(fhp)); if (error) return(error); /* find the mount point */ mp = vfs_getvfs(&fhp.fh_fsid); if (mp == NULL) return (ESTALE); /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, &vp); if (error) return (error); /* * from now on we have to make sure not * to forget about the vnode * any error that causes an abort must vput(vp) * just set error = err and 'goto bad;'. */ /* * from vn_open */ if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (fmode & O_APPEND) mode |= VAPPEND; #ifdef MAC error = mac_check_vnode_open(td->td_ucred, vp, mode); if (error) goto bad; #endif if (mode) { error = VOP_ACCESS(vp, mode, td->td_ucred, td); if (error) goto bad; } if (fmode & O_TRUNC) { VOP_UNLOCK(vp, 0, td); /* XXX */ if ((error = vn_start_write(NULL, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); /* XXX */ #ifdef MAC /* * We don't yet have fp->f_cred, so use td->td_ucred, which * should be right. */ error = mac_check_vnode_write(td->td_ucred, td->td_ucred, vp); if (error == 0) { #endif VATTR_NULL(vap); vap->va_size = 0; error = VOP_SETATTR(vp, vap, td->td_ucred, td); #ifdef MAC } #endif vn_finished_write(mp); if (error) goto bad; } error = VOP_OPEN(vp, fmode, td->td_ucred, td, -1); if (error) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, td, td->td_ucred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; /* * end of vn_open code */ if ((error = falloc(td, &nfp, &indx)) != 0) { if (fmode & FWRITE) vp->v_writecount--; goto bad; } /* An extra reference on `nfp' has been held for us by falloc(). */ fp = nfp; nfp->f_vnode = vp; nfp->f_data = vp; nfp->f_flag = fmode & FMASK; nfp->f_ops = &vnops; nfp->f_type = DTYPE_VNODE; if (fmode & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (fmode & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((fmode & FNONBLOCK) == 0) type |= F_WAIT; VOP_UNLOCK(vp, 0, td); if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) { /* * The lock request failed. Normally close the * descriptor but handle the case where someone might * have dup()d or close()d it when we weren't looking. */ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdunused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else { FILEDESC_UNLOCK(fdp); } /* * release our private reference */ fdrop(fp, td); return(error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); fp->f_flag |= FHASLOCK; } if ((vp->v_type == VREG) && (VOP_GETVOBJECT(vp, NULL) != 0)) vfs_object_create(vp, td, td->td_ucred); VOP_UNLOCK(vp, 0, td); fdrop(fp, td); td->td_retval[0] = indx; return (0); bad: vput(vp); return (error); } /* * Stat an (NFS) file handle. */ #ifndef _SYS_SYSPROTO_H_ struct fhstat_args { struct fhandle *u_fhp; struct stat *sb; }; #endif int fhstat(td, uap) struct thread *td; register struct fhstat_args /* { struct fhandle *u_fhp; struct stat *sb; } */ *uap; { struct stat sb; fhandle_t fh; struct mount *mp; struct vnode *vp; int error; error = suser(td); if (error) return (error); error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); if (error) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); vput(vp); if (error) return (error); error = copyout(&sb, uap->sb, sizeof(sb)); return (error); } /* * Implement fstatfs() for (NFS) file handles. */ #ifndef _SYS_SYSPROTO_H_ struct fhstatfs_args { struct fhandle *u_fhp; struct statfs *buf; }; #endif int fhstatfs(td, uap) struct thread *td; struct fhstatfs_args /* { struct fhandle *u_fhp; struct statfs *buf; } */ *uap; { struct statfs *sp; struct mount *mp; struct vnode *vp; fhandle_t fh; int error; error = suser(td); if (error) return (error); if ((error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t))) != 0) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) return (error); #endif /* * Set these in case the underlying filesystem fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if ((error = VFS_STATFS(mp, sp, td)) != 0) return (error); return (copyout(sp, uap->buf, sizeof(*sp))); } /* * Syscall to push extended attribute configuration information into the * VFS. Accepts a path, which it converts to a mountpoint, as well as * a command (int cmd), and attribute name and misc data. For now, the * attribute name is left in userspace for consumption by the VFS_op. * It will probably be changed to be copied into sysspace by the * syscall in the future, once issues with various consumers of the * attribute code have raised their hands. * * Currently this is used only by UFS Extended Attributes. */ int extattrctl(td, uap) struct thread *td; struct extattrctl_args /* { const char *path; int cmd; const char *filename; int attrnamespace; const char *attrname; } */ *uap; { struct vnode *filename_vp; struct nameidata nd; struct mount *mp, *mp_writable; char attrname[EXTATTR_MAXNAMELEN]; int error; /* * uap->attrname is not always defined. We check again later when we * invoke the VFS call so as to pass in NULL there if needed. */ if (uap->attrname != NULL) { error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); } /* * uap->filename is not always defined. If it is, grab a vnode lock, * which VFS_EXTATTRCTL() will later release. */ filename_vp = NULL; if (uap->filename != NULL) { NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->filename, td); error = namei(&nd); if (error) return (error); filename_vp = nd.ni_vp; NDFREE(&nd, NDF_NO_VP_RELE | NDF_NO_VP_UNLOCK); } /* uap->path is always defined. */ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) { if (filename_vp != NULL) vput(filename_vp); return (error); } mp = nd.ni_vp->v_mount; error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | PCATCH); NDFREE(&nd, 0); if (error) { if (filename_vp != NULL) vput(filename_vp); return (error); } error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace, uap->attrname != NULL ? attrname : NULL, td); vn_finished_write(mp_writable); /* * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, * filename_vp, so vrele it if it is defined. */ if (filename_vp != NULL) vrele(filename_vp); return (error); } /*- * Set a named extended attribute on a file or directory * * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", * kernelspace string pointer "attrname", userspace buffer * pointer "data", buffer length "nbytes", thread "td". * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ static int extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname, void *data, size_t nbytes, struct thread *td) { struct mount *mp; struct uio auio; struct iovec aiov; ssize_t cnt; int error; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); aiov.iov_base = data; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; if (nbytes > INT_MAX) { error = EINVAL; goto done; } auio.uio_resid = nbytes; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; cnt = nbytes; #ifdef MAC error = mac_check_vnode_setextattr(td->td_ucred, vp, attrnamespace, attrname, &auio); if (error) goto done; #endif error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, td->td_ucred, td); cnt -= auio.uio_resid; td->td_retval[0] = cnt; done: VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } int extattr_set_fd(td, uap) struct thread *td; struct extattr_set_fd_args /* { int fd; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); error = getvnode(td->td_proc->p_fd, uap->fd, &fp); if (error) return (error); error = extattr_set_vp(fp->f_vnode, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); fdrop(fp, td); return (error); } int extattr_set_file(td, uap) struct thread *td; struct extattr_set_file_args /* { const char *path; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } int extattr_set_link(td, uap) struct thread *td; struct extattr_set_link_args /* { const char *path; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } /*- * Get a named extended attribute on a file or directory * * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", * kernelspace string pointer "attrname", userspace buffer * pointer "data", buffer length "nbytes", thread "td". * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ static int extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, void *data, size_t nbytes, struct thread *td) { struct uio auio, *auiop; struct iovec aiov; ssize_t cnt; size_t size, *sizep; int error; /* * XXX: Temporary API compatibility for applications that know * about this hack ("" means list), but haven't been updated * for the extattr_list_*() system calls yet. This will go * away for FreeBSD 5.3. */ if (strlen(attrname) == 0) return (extattr_list_vp(vp, attrnamespace, data, nbytes, td)); VOP_LEASE(vp, td, td->td_ucred, LEASE_READ); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); /* * Slightly unusual semantics: if the user provides a NULL data * pointer, they don't want to receive the data, just the * maximum read length. */ auiop = NULL; sizep = NULL; cnt = 0; if (data != NULL) { aiov.iov_base = data; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_offset = 0; if (nbytes > INT_MAX) { error = EINVAL; goto done; } auio.uio_resid = nbytes; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; auiop = &auio; cnt = nbytes; } else { sizep = &size; } #ifdef MAC error = mac_check_vnode_getextattr(td->td_ucred, vp, attrnamespace, attrname, &auio); if (error) goto done; #endif error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep, td->td_ucred, td); if (auiop != NULL) { cnt -= auio.uio_resid; td->td_retval[0] = cnt; } else { td->td_retval[0] = size; } done: VOP_UNLOCK(vp, 0, td); return (error); } int extattr_get_fd(td, uap) struct thread *td; struct extattr_get_fd_args /* { int fd; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); error = getvnode(td->td_proc->p_fd, uap->fd, &fp); if (error) return (error); error = extattr_get_vp(fp->f_vnode, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); fdrop(fp, td); return (error); } int extattr_get_file(td, uap) struct thread *td; struct extattr_get_file_args /* { const char *path; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } int extattr_get_link(td, uap) struct thread *td; struct extattr_get_link_args /* { const char *path; int attrnamespace; const char *attrname; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } /* * extattr_delete_vp(): Delete a named extended attribute on a file or * directory * * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", * kernelspace string pointer "attrname", proc "p" * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ static int extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, struct thread *td) { struct mount *mp; int error; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); #ifdef MAC error = mac_check_vnode_deleteextattr(td->td_ucred, vp, attrnamespace, attrname); if (error) goto done; #endif error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred, td); if (error == EOPNOTSUPP) error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, td->td_ucred, td); #ifdef MAC done: #endif VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); return (error); } int extattr_delete_fd(td, uap) struct thread *td; struct extattr_delete_fd_args /* { int fd; int attrnamespace; const char *attrname; } */ *uap; { struct file *fp; struct vnode *vp; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return (error); error = getvnode(td->td_proc->p_fd, uap->fd, &fp); if (error) return (error); vp = fp->f_vnode; error = extattr_delete_vp(vp, uap->attrnamespace, attrname, td); fdrop(fp, td); return (error); } int extattr_delete_file(td, uap) struct thread *td; struct extattr_delete_file_args /* { const char *path; int attrnamespace; const char *attrname; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return(error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return(error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); vrele(nd.ni_vp); return(error); } int extattr_delete_link(td, uap) struct thread *td; struct extattr_delete_link_args /* { const char *path; int attrnamespace; const char *attrname; } */ *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) return(error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return(error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); vrele(nd.ni_vp); return(error); } /*- * Retrieve a list of extended attributes on a file or directory. * * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace", * userspace buffer pointer "data", buffer length "nbytes", * thread "td". * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ static int extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, size_t nbytes, struct thread *td) { struct uio auio, *auiop; size_t size, *sizep; struct iovec aiov; ssize_t cnt; int error; VOP_LEASE(vp, td, td->td_ucred, LEASE_READ); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); auiop = NULL; sizep = NULL; cnt = 0; if (data != NULL) { aiov.iov_base = data; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_offset = 0; if (nbytes > INT_MAX) { error = EINVAL; goto done; } auio.uio_resid = nbytes; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; auiop = &auio; cnt = nbytes; } else { sizep = &size; } #ifdef MAC error = mac_check_vnode_listextattr(td->td_ucred, vp, attrnamespace); if (error) goto done; #endif error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep, td->td_ucred, td); if (auiop != NULL) { cnt -= auio.uio_resid; td->td_retval[0] = cnt; } else { td->td_retval[0] = size; } done: VOP_UNLOCK(vp, 0, td); return (error); } int extattr_list_fd(td, uap) struct thread *td; struct extattr_list_fd_args /* { int fd; int attrnamespace; void *data; size_t nbytes; } */ *uap; { struct file *fp; int error; error = getvnode(td->td_proc->p_fd, uap->fd, &fp); if (error) return (error); error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data, uap->nbytes, td); fdrop(fp, td); return (error); } int extattr_list_file(td, uap) struct thread*td; struct extattr_list_file_args /* { const char *path; int attrnamespace; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; int error; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } int extattr_list_link(td, uap) struct thread*td; struct extattr_list_link_args /* { const char *path; int attrnamespace; void *data; size_t nbytes; } */ *uap; { struct nameidata nd; int error; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, uap->nbytes, td); vrele(nd.ni_vp); return (error); } Index: head/sys/pc98/cbus/sio.c =================================================================== --- head/sys/pc98/cbus/sio.c (revision 130343) +++ head/sys/pc98/cbus/sio.c (revision 130344) @@ -1,5043 +1,5043 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * 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. * 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. * * $FreeBSD$ * from: @(#)com.c 7.5 (Berkeley) 5/16/91 * from: i386/isa sio.c,v 1.234 */ #include "opt_comconsole.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_sio.h" /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ /*=============================================================== * 386BSD(98),FreeBSD-1.1x(98) com driver. * ----- * modified for PC9801 by M.Ishii * Kyoto University Microcomputer Club (KMC) * Chou "TEFUTEFU" Hirotomi * Kyoto Univ. the faculty of medicine *=============================================================== * FreeBSD-2.0.1(98) sio driver. * ----- * modified for pc98 Internal i8251 and MICRO CORE MC16550II * T.Koike(hfc01340@niftyserve.or.jp) * implement kernel device configuration * aizu@orient.center.nitech.ac.jp * * Notes. * ----- * PC98 localization based on 386BSD(98) com driver. Using its PC98 local * functions. * This driver is under debugging,has bugs. */ /* * modified for AIWA B98-01 * by T.Hatanou last update: 15 Sep.1995 */ /* * Modified by Y.Takahashi of Kogakuin University. */ /* * modified for 8251(FIFO) by Seigo TANIMURA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if DDB > 0 #include #endif #include #include #include #include #ifdef PC98 #include #include #endif #ifdef COM_ESP #include #endif #include #ifdef PC98 #include #include #endif #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_TO_UNIT(mynor) ((((mynor) & ~0xffffU) >> (8 + 3)) \ | ((mynor) & 0x1f)) #define UNIT_TO_MINOR(unit) ((((unit) & ~0x1fU) << (8 + 3)) \ | ((unit) & 0x1f)) /* * Meaning of flags: * * 0x00000001 shared IRQs * 0x00000002 disable FIFO * 0x00000008 recover sooner from lost output interrupts * 0x00000010 device is potential system console * 0x00000020 device is forced to become system console * 0x00000040 device is reserved for low-level IO * 0x00000080 use this port for remote kernel debugging * 0x0000??00 minor number of master port * 0x00010000 PPS timestamping on CTS instead of DCD * 0x00080000 IIR_TXRDY bug * 0x00400000 If no comconsole found then mark as a comconsole * 0x1?000000 interface type */ #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(flags) ((flags) & 0x01) #define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) #ifndef PC98 #define COM_NOTAST4(flags) ((flags) & 0x04) #endif #else #define COM_ISMULTIPORT(flags) (0) #endif /* COM_MULTIPORT */ #define COM_C_IIR_TXRDYBUG 0x80000 #define COM_CONSOLE(flags) ((flags) & 0x10) #define COM_DEBUGGER(flags) ((flags) & 0x80) #ifndef PC98 #define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) #endif #define COM_FORCECONSOLE(flags) ((flags) & 0x20) #define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) #define COM_LLCONSOLE(flags) ((flags) & 0x40) #define COM_LOSESOUTINTS(flags) ((flags) & 0x08) #define COM_NOFIFO(flags) ((flags) & 0x02) #ifndef PC98 #define COM_NOSCR(flags) ((flags) & 0x100000) #endif #define COM_PPSCTS(flags) ((flags) & 0x10000) #ifndef PC98 #define COM_ST16650A(flags) ((flags) & 0x20000) #define COM_TI16754(flags) ((flags) & 0x200000) #endif #define sio_getreg(com, off) \ (bus_space_read_1((com)->bst, (com)->bsh, (off))) #define sio_setreg(com, off, value) \ (bus_space_write_1((com)->bst, (com)->bsh, (off), (value))) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * comstop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ bool_t st16650a; /* nonzero if Startech 16650A compatible */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int flags; /* copy of device flags */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ibufold; /* old input buffer, to be freed */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ int ibufsize; /* size of ibuf (not include error bytes) */ int ierroff; /* offset of error bytes in ibuf */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ bus_space_tag_t bst; bus_space_handle_t bsh; #ifdef PC98 Port_t cmd_port; Port_t sts_port; Port_t in_modem_port; Port_t intr_ctrl_port; Port_t rsabase; /* Iobase address of an I/O-DATA RSA board. */ int intr_enable; int pc98_prev_modem_status; int pc98_modem_delta; int modem_car_chg_timer; int pc98_prev_siocmd; int pc98_prev_siomod; int modem_checking; int pc98_if_type; bool_t pc98_8251fifo; bool_t pc98_8251fifo_enable; #endif /* PC98 */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_ctl_port; Port_t int_id_port; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; struct pps_state pps; int pps_bit; #ifdef ALT_BREAK_TO_DEBUGGER int alt_brk_state; #endif u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; u_long rclk; struct resource *irqres; struct resource *ioportres; int ioportrid; void *cookie; dev_t devs[6]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ #ifdef PC98 int obufsize; u_char *obuf1; u_char *obuf2; #else u_char obuf1[256]; u_char obuf2[256]; #endif }; #ifdef COM_ESP static int espattach(struct com_s *com, Port_t esp_port); #endif static timeout_t siobusycheck; static u_int siodivisor(u_long rclk, speed_t speed); static timeout_t siodtrwakeup; static void comhardclose(struct com_s *com); static void sioinput(struct com_s *com); static void siointr1(struct com_s *com); static void siointr(void *arg); static int commctl(struct com_s *com, int bits, int how); static int comparam(struct tty *tp, struct termios *t); static void siopoll(void *); static void siosettimeout(void); static int siosetwater(struct com_s *com, speed_t speed); static void comstart(struct tty *tp); static void comstop(struct tty *tp, int rw); static timeout_t comwakeup; char sio_driver_name[] = "sio"; static struct mtx sio_lock; static int sio_inited; /* table and macro for fast conversion from a unit number to its com struct */ devclass_t sio_devclass; #define com_addr(unit) ((struct com_s *) \ devclass_get_softc(sio_devclass, unit)) /* XXX */ static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static struct cdevsw sio_cdevsw = { .d_version = D_VERSION, .d_open = sioopen, .d_close = sioclose, .d_read = sioread, .d_write = siowrite, .d_ioctl = sioioctl, .d_name = sio_driver_name, .d_flags = D_TTY | D_NEEDGIANT, }; int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; static u_long comdefaultrclk = DEFAULT_RCLK; SYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, ""); static speed_t gdbdefaultrate = GDBSPEED; SYSCTL_UINT(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW, &gdbdefaultrate, GDBSPEED, ""); static u_int com_events; /* input chars + weighted output completions */ static Port_t siocniobase; static int siocnunit = -1; static Port_t siogdbiobase; static int siogdbunit = -1; static void *sio_slow_ih; static void *sio_fast_ih; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); static int sio_numunits; #ifdef PC98 struct siodev { short if_type; short irq; Port_t cmd, sts, ctrl, mod; }; static int sysclock; #define COM_INT_DISABLE {int previpri; previpri=spltty(); #define COM_INT_ENABLE splx(previpri);} #define IEN_TxFLAG IEN_Tx #define COM_CARRIER_DETECT_EMULATE 0 #define PC98_CHECK_MODEM_INTERVAL (hz/10) #define DCD_OFF_TOLERANCE 2 #define DCD_ON_RECOGNITION 2 #define IS_8251(if_type) (!(if_type & 0x10)) #define COM1_EXT_CLOCK 0x40000 static void commint(dev_t dev); static void com_tiocm_set(struct com_s *com, int msr); static void com_tiocm_bis(struct com_s *com, int msr); static void com_tiocm_bic(struct com_s *com, int msr); static int com_tiocm_get(struct com_s *com); static int com_tiocm_get_delta(struct com_s *com); static void pc98_msrint_start(dev_t dev); static void com_cflag_and_speed_set(struct com_s *com, int cflag, int speed); static int pc98_ttspeedtab(struct com_s *com, int speed, u_int *divisor); static int pc98_get_modem_status(struct com_s *com); static timeout_t pc98_check_msr; static void pc98_set_baud_rate(struct com_s *com, u_int count); static void pc98_i8251_reset(struct com_s *com, int mode, int command); static void pc98_disable_i8251_interrupt(struct com_s *com, int mod); static void pc98_enable_i8251_interrupt(struct com_s *com, int mod); static int pc98_check_i8251_interrupt(struct com_s *com); static int pc98_i8251_get_cmd(struct com_s *com); static int pc98_i8251_get_mod(struct com_s *com); static void pc98_i8251_set_cmd(struct com_s *com, int x); static void pc98_i8251_or_cmd(struct com_s *com, int x); static void pc98_i8251_clear_cmd(struct com_s *com, int x); static void pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x); static int pc98_check_if_type(device_t dev, struct siodev *iod); static int pc98_check_8251vfast(void); static int pc98_check_8251fifo(void); static void pc98_check_sysclock(void); static void pc98_set_ioport(struct com_s *com); #define com_int_Tx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP) #define com_int_Tx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_TxFLAG) #define com_int_Rx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Rx) #define com_int_Rx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_Rx) #define com_int_TxRx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP|IEN_Rx) #define com_int_TxRx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_TxFLAG|IEN_Rx) #define com_send_break_on(com) \ pc98_i8251_or_cmd(com,CMD8251_SBRK) #define com_send_break_off(com) \ pc98_i8251_clear_cmd(com,CMD8251_SBRK) static struct speedtab pc98speedtab[] = { /* internal RS232C interface */ { 0, 0, }, { 50, 50, }, { 75, 75, }, { 150, 150, }, { 200, 200, }, { 300, 300, }, { 600, 600, }, { 1200, 1200, }, { 2400, 2400, }, { 4800, 4800, }, { 9600, 9600, }, { 19200, 19200, }, { 38400, 38400, }, { 51200, 51200, }, { 76800, 76800, }, { 20800, 20800, }, { 31200, 31200, }, { 41600, 41600, }, { 62400, 62400, }, { -1, -1 } }; static struct speedtab pc98fast_speedtab[] = { { 9600, 0x80 | (DEFAULT_RCLK / (16 * (9600))), }, { 19200, 0x80 | (DEFAULT_RCLK / (16 * (19200))), }, { 38400, 0x80 | (DEFAULT_RCLK / (16 * (38400))), }, { 57600, 0x80 | (DEFAULT_RCLK / (16 * (57600))), }, { 115200, 0x80 | (DEFAULT_RCLK / (16 * (115200))), }, { -1, -1 } }; static struct speedtab comspeedtab_pio9032b[] = { { 300, 6, }, { 600, 5, }, { 1200, 4, }, { 2400, 3, }, { 4800, 2, }, { 9600, 1, }, { 19200, 0, }, { 38400, 7, }, { -1, -1 } }; static struct speedtab comspeedtab_b98_01[] = { { 75, 11, }, { 150, 10, }, { 300, 9, }, { 600, 8, }, { 1200, 7, }, { 2400, 6, }, { 4800, 5, }, { 9600, 4, }, { 19200, 3, }, { 38400, 2, }, { 76800, 1, }, { 153600, 0, }, { -1, -1 } }; static struct speedtab comspeedtab_ind[] = { { 300, 1536, }, { 600, 768, }, { 1200, 384, }, { 2400, 192, }, { 4800, 96, }, { 9600, 48, }, { 19200, 24, }, { 38400, 12, }, { 57600, 8, }, { 115200, 4, }, { 153600, 3, }, { 230400, 2, }, { 460800, 1, }, { -1, -1 } }; struct { char *name; short port_table[7]; short irr_mask; struct speedtab *speedtab; short check_irq; } if_8251_type[] = { /* COM_IF_INTERNAL */ { " (internal)", {0x30, 0x32, 0x32, 0x33, 0x35, -1, -1}, -1, pc98speedtab, 1 }, /* COM_IF_PC9861K_1 */ { " (PC9861K)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, -1, -1}, 3, NULL, 1 }, /* COM_IF_PC9861K_2 */ { " (PC9861K)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, -1, -1}, 3, NULL, 1 }, /* COM_IF_IND_SS_1 */ { " (IND-SS)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb3, -1}, 3, comspeedtab_ind, 1 }, /* COM_IF_IND_SS_2 */ { " (IND-SS)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xbb, -1}, 3, comspeedtab_ind, 1 }, /* COM_IF_PIO9032B_1 */ { " (PIO9032B)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb8, -1}, 7, comspeedtab_pio9032b, 1 }, /* COM_IF_PIO9032B_2 */ { " (PIO9032B)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xba, -1}, 7, comspeedtab_pio9032b, 1 }, /* COM_IF_B98_01_1 */ { " (B98-01)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xd1, 0xd3}, 7, comspeedtab_b98_01, 0 }, /* COM_IF_B98_01_2 */ { " (B98-01)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xd5, 0xd7}, 7, comspeedtab_b98_01, 0 }, }; #define PC98SIO_data_port(type) (if_8251_type[type].port_table[0]) #define PC98SIO_cmd_port(type) (if_8251_type[type].port_table[1]) #define PC98SIO_sts_port(type) (if_8251_type[type].port_table[2]) #define PC98SIO_in_modem_port(type) (if_8251_type[type].port_table[3]) #define PC98SIO_intr_ctrl_port(type) (if_8251_type[type].port_table[4]) #define PC98SIO_baud_rate_port(type) (if_8251_type[type].port_table[5]) #define PC98SIO_func_port(type) (if_8251_type[type].port_table[6]) #define I8251F_data 0x130 #define I8251F_lsr 0x132 #define I8251F_msr 0x134 #define I8251F_iir 0x136 #define I8251F_fcr 0x138 #define I8251F_div 0x13a static bus_addr_t port_table_0[] = {0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007}; static bus_addr_t port_table_1[] = {0x000, 0x002, 0x004, 0x006, 0x008, 0x00a, 0x00c, 0x00e}; static bus_addr_t port_table_8[] = {0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700}; static bus_addr_t port_table_rsa[] = { 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007 }; struct { char *name; short irr_read; short irr_write; bus_addr_t *iat; bus_size_t iatsz; u_long rclk; } if_16550a_type[] = { /* COM_IF_RSA98 */ {" (RSA-98)", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, /* COM_IF_NS16550 */ {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, /* COM_IF_SECOND_CCU */ {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, /* COM_IF_MC16550II */ {" (MC16550II)", -1, 0x1000, port_table_8, IO_COMSIZE, DEFAULT_RCLK * 4}, /* COM_IF_MCRS98 */ {" (MC-RS98)", -1, 0x1000, port_table_8, IO_COMSIZE, DEFAULT_RCLK * 4}, /* COM_IF_RSB3000 */ {" (RSB-3000)", 0xbf, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 10}, /* COM_IF_RSB384 */ {" (RSB-384)", 0xbf, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 10}, /* COM_IF_MODEM_CARD */ {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, /* COM_IF_RSA98III */ {" (RSA-98III)", -1, -1, port_table_rsa, 16, DEFAULT_RCLK * 8}, /* COM_IF_ESP98 */ {" (ESP98)", -1, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 4}, }; #endif /* PC98 */ #ifdef COM_ESP #ifdef PC98 /* XXX configure this properly. */ /* XXX quite broken for new-bus. */ static Port_t likely_com_ports[] = { 0, 0xb0, 0xb1, 0 }; static Port_t likely_esp_ports[] = { 0xc0d0, 0 }; #define ESP98_CMD1 (ESP_CMD1 * 0x100) #define ESP98_CMD2 (ESP_CMD2 * 0x100) #define ESP98_STATUS1 (ESP_STATUS1 * 0x100) #define ESP98_STATUS2 (ESP_STATUS2 * 0x100) #else /* PC98 */ /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif /* PC98 */ #endif /* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */ static int sysctl_machdep_comdefaultrate(SYSCTL_HANDLER_ARGS) { int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (com == NULL) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error; } SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", ""); /* * Unload the driver and clear the table. * XXX this is mostly wrong. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a kldunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ int siodetach(dev) device_t dev; { struct com_s *com; int i; com = (struct com_s *) device_get_softc(dev); if (com == NULL) { device_printf(dev, "NULL com in siounload\n"); return (0); } com->gone = TRUE; for (i = 0 ; i < 6; i++) destroy_dev(com->devs[i]); if (com->irqres) { bus_teardown_intr(dev, com->irqres, com->cookie); bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres); } if (com->ioportres) bus_release_resource(dev, SYS_RES_IOPORT, com->ioportrid, com->ioportres); if (com->tp && (com->tp->t_state & TS_ISOPEN)) { device_printf(dev, "still open, forcing close\n"); ttyld_close(com->tp, 0); ttyclose(com->tp); } else { if (com->ibuf != NULL) free(com->ibuf, M_DEVBUF); #ifdef PC98 if (com->obuf1 != NULL) free(com->obuf1, M_DEVBUF); #endif device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (0); } int sioprobe(dev, xrid, rclk, noprobe) device_t dev; int xrid; u_long rclk; int noprobe; { #if 0 static bool_t already_init; device_t xdev; #endif struct com_s *com; u_int divisor; bool_t failures[10]; int fn; device_t idev; Port_t iobase; intrmask_t irqmap[4]; intrmask_t irqs; u_char mcr_image; int result; u_long xirq; u_int flags = device_get_flags(dev); int rid; struct resource *port; #ifdef PC98 int tmp; struct siodev iod; #endif #ifdef PC98 iod.if_type = GET_IFTYPE(flags); if ((iod.if_type < 0 || iod.if_type > COM_IF_END1) && (iod.if_type < 0x10 || iod.if_type > COM_IF_END2)) return ENXIO; #endif rid = xrid; #ifdef PC98 if (IS_8251(iod.if_type)) { port = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); } else if (iod.if_type == COM_IF_MODEM_CARD || iod.if_type == COM_IF_RSA98III || isa_get_vendorid(dev)) { port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, if_16550a_type[iod.if_type & 0x0f].iatsz, RF_ACTIVE); } else { port = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, if_16550a_type[iod.if_type & 0x0f].iat, if_16550a_type[iod.if_type & 0x0f].iatsz, RF_ACTIVE); } #else port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_COMSIZE, RF_ACTIVE); #endif if (!port) return (ENXIO); #ifdef PC98 if (!IS_8251(iod.if_type)) { if (isa_load_resourcev(port, if_16550a_type[iod.if_type & 0x0f].iat, if_16550a_type[iod.if_type & 0x0f].iatsz) != 0) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return ENXIO; } } #endif com = malloc(sizeof(*com), M_DEVBUF, M_NOWAIT | M_ZERO); if (com == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return (ENOMEM); } device_set_softc(dev, com); com->bst = rman_get_bustag(port); com->bsh = rman_get_bushandle(port); #ifdef PC98 if (!IS_8251(iod.if_type) && rclk == 0) rclk = if_16550a_type[iod.if_type & 0x0f].rclk; #else if (rclk == 0) rclk = DEFAULT_RCLK; #endif com->rclk = rclk; while (sio_inited != 2) if (atomic_cmpset_int(&sio_inited, 0, 1)) { mtx_init(&sio_lock, sio_driver_name, NULL, (comconsole != -1) ? MTX_SPIN | MTX_QUIET : MTX_SPIN); atomic_store_rel_int(&sio_inited, 2); } #if 0 /* * XXX this is broken - when we are first called, there are no * previously configured IO ports. We could hard code * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse. * This code has been doing nothing since the conversion since * "count" is zero the first time around. */ if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ device_t *devs; int count, i, xioport; #ifdef PC98 int xiftype; #endif devclass_get_devices(sio_devclass, &devs, &count); #ifdef PC98 for (i = 0; i < count; i++) { xdev = devs[i]; xioport = bus_get_resource_start(xdev, SYS_RES_IOPORT, 0); xiftype = GET_IFTYPE(device_get_flags(xdev)); if (device_is_enabled(xdev) && xioport > 0) { if (IS_8251(xiftype)) outb((xioport & 0xff00) | PC98SIO_cmd_port(xiftype & 0x0f), 0xf2); else outb(xioport + if_16550a_type[xiftype & 0x0f].iat[com_mcr], 0); } } #else for (i = 0; i < count; i++) { xdev = devs[i]; if (device_is_enabled(xdev) && bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport, NULL) == 0) outb(xioport + com_mcr, 0); } #endif free(devs, M_TEMP); already_init = TRUE; } #endif if (COM_LLCONSOLE(flags)) { printf("sio%d: reserved for low-level i/o\n", device_get_unit(dev)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } #ifdef PC98 DELAY(10); /* * If the port is i8251 UART (internal, B98_01) */ if (pc98_check_if_type(dev, &iod) == -1) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } if (iod.irq > 0) bus_set_resource(dev, SYS_RES_IRQ, 0, iod.irq, 1); if (IS_8251(iod.if_type)) { outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, CMD8251_RESET); DELAY(1000); /* for a while...*/ outb(iod.cmd, 0xf2); /* MODE (dummy) */ DELAY(10); outb(iod.cmd, 0x01); /* CMD (dummy) */ DELAY(1000); /* for a while...*/ if (( inb(iod.sts) & STS8251_TxEMP ) == 0 ) { result = (ENXIO); } if (if_8251_type[iod.if_type & 0x0f].check_irq) { COM_INT_DISABLE tmp = ( inb( iod.ctrl ) & ~(IEN_Rx|IEN_TxEMP|IEN_Tx)); outb( iod.ctrl, tmp|IEN_TxEMP ); DELAY(10); result = isa_irq_pending() ? 0 : ENXIO; outb( iod.ctrl, tmp ); COM_INT_ENABLE } else { /* * B98_01 doesn't activate TxEMP interrupt line * when being reset, so we can't check irq pending. */ result = 0; } if (epson_machine_id==0x20) { /* XXX */ result = 0; } bus_release_resource(dev, SYS_RES_IOPORT, rid, port); if (result) { device_set_softc(dev, NULL); free(com, M_DEVBUF); } return result; } #endif /* PC98 */ /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { #ifndef PC98 Port_t xiobase; u_long io; #endif idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", device_get_unit(dev), COM_MPMASTER(flags)); idev = dev; } #ifndef PC98 if (!COM_NOTAST4(flags)) { if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io, NULL) == 0) { xiobase = io; if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) == 0) outb(xiobase + com_scr, 0x80); else outb(xiobase + com_scr, 0); } mcr_image = 0; } #endif } #endif /* COM_MULTIPORT */ if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = rman_get_start(port); #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) { mcr_image = 0; outb(iobase + rsa_msr, 0x04); outb(iobase + rsa_frr, 0x00); if ((inb(iobase + rsa_srr) & 0x36) != 0x36) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } outb(iobase + rsa_ier, 0x00); outb(iobase + rsa_frr, 0x00); outb(iobase + rsa_tivsr, 0x00); outb(iobase + rsa_tcr, 0x00); } tmp = if_16550a_type[iod.if_type & 0x0f].irr_write; if (tmp != -1) { /* MC16550II */ int irqout; switch (isa_get_irq(idev)) { case 3: irqout = 4; break; case 5: irqout = 5; break; case 6: irqout = 6; break; case 12: irqout = 7; break; default: printf("sio%d: irq configuration error\n", device_get_unit(dev)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } outb((iobase & 0x00ff) | tmp, irqout); } #endif /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ mtx_lock_spin(&sio_lock); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ if (iobase == siocniobase) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS); divisor = siodivisor(rclk, SIO_TEST_SPEED); sio_setreg(com, com_dlbl, divisor & 0xff); sio_setreg(com, com_dlbh, divisor >> 8); sio_setreg(com, com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image); sio_setreg(com, com_ier, 0); DELAY(1000); /* XXX */ irqmap[0] = isa_irq_pending(); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ sio_setreg(com, com_ier, IER_ETXRDY); #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) outb(iobase + rsa_ier, 0x04); #endif /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ sio_setreg(com, com_data, 0); if (iobase == siocniobase) DELAY((1 + 2) * 1000000 / (comdefaultrate / 10)); else DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image); /* * It seems my Xircom CBEM56G Cardbus modem wants to be reset * to 8 bits *again*, or else probe test 0 will fail. * gwk@sgi.com, 4/19/2001 */ sio_setreg(com, com_cfcr, CFCR_8BITS); /* * Some PCMCIA cards (Palido 321s, DC-1S, ...) have the "TXRDY bug", * so we probe for a buggy IIR_TXRDY implementation even in the * noprobe case. We don't probe for it in the !noprobe case because * noprobe is always set for PCMCIA cards and the problem is not * known to affect any other cards. */ if (noprobe) { /* Read IIR a few times. */ for (fn = 0; fn < 2; fn ++) { DELAY(10000); failures[6] = sio_getreg(com, com_iir); } /* IIR_TXRDY should be clear. Is it? */ result = 0; if (failures[6] & IIR_TXRDY) { /* * No. We seem to have the bug. Does our fix for * it work? */ sio_setreg(com, com_ier, 0); if (sio_getreg(com, com_iir) & IIR_NOPEND) { /* Yes. We discovered the TXRDY bug! */ SET_FLAG(dev, COM_C_IIR_TXRDYBUG); } else { /* No. Just fail. XXX */ result = ENXIO; sio_setreg(com, com_mcr, 0); } } else { /* Yes. No bug. */ CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); } sio_setreg(com, com_ier, 0); sio_setreg(com, com_cfcr, CFCR_8BITS); mtx_unlock_spin(&sio_lock); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); if (iobase == siocniobase) result = 0; if (result != 0) { device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (result); } /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS; failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY; failures[2] = sio_getreg(com, com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ irqmap[1] = isa_irq_pending(); failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY; #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) inb(iobase + rsa_srr); #endif DELAY(1000); /* XXX */ irqmap[2] = isa_irq_pending(); failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) inb(iobase + rsa_srr); #endif /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving it) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ sio_setreg(com, com_ier, 0); sio_setreg(com, com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = sio_getreg(com, com_ier); #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) outb(iobase + rsa_ier, 0x00); #endif DELAY(1000); /* XXX */ irqmap[3] = isa_irq_pending(); failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) { inb(iobase + rsa_srr); outb(iobase + rsa_frr, 0x00); } #endif mtx_unlock_spin(&sio_lock); irqs = irqmap[1] & ~irqmap[0]; if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 && ((1 << xirq) & irqs) == 0) { printf( "sio%d: configured irq %ld not in bitmap of probed irqs %#x\n", device_get_unit(dev), xirq, irqs); printf( "sio%d: port may not be enabled\n", device_get_unit(dev)); } if (bootverbose) printf("sio%d: irq maps: %#x %#x %#x %#x\n", device_get_unit(dev), irqmap[0], irqmap[1], irqmap[2], irqmap[3]); result = 0; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { sio_setreg(com, com_mcr, 0); result = ENXIO; if (bootverbose) { printf("sio%d: probe failed test(s):", device_get_unit(dev)); for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) printf(" %d", fn); printf("\n"); } break; } bus_release_resource(dev, SYS_RES_IOPORT, rid, port); if (iobase == siocniobase) result = 0; if (result != 0) { device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (result); } #ifdef COM_ESP static int espattach(com, esp_port) struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ #ifdef PC98 outb(esp_port + ESP98_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP98_STATUS1); #else outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); #endif /* * Bits 0,1 of dips say which COM port we are. */ #ifdef PC98 if ((rman_get_start(com->ioportres) & 0xff) == likely_com_ports[dips & 0x03]) #else if (rman_get_start(com->ioportres) == likely_com_ports[dips & 0x03]) #endif printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ #ifdef PC98 outb(esp_port + ESP98_CMD1, ESP_GETTEST); val = inb(esp_port + ESP98_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP98_STATUS2); #else outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); #endif if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ int sioattach(dev, xrid, rclk) device_t dev; int xrid; u_long rclk; { struct com_s *com; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int minorbase; int unit; u_int flags; int rid; struct resource *port; int ret; #ifdef PC98 u_char *obuf; u_long obufsize; int if_type = GET_IFTYPE(device_get_flags(dev)); #endif rid = xrid; #ifdef PC98 if (IS_8251(if_type)) { port = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); } else if (if_type == COM_IF_MODEM_CARD || if_type == COM_IF_RSA98III || isa_get_vendorid(dev)) { port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, if_16550a_type[if_type & 0x0f].iatsz, RF_ACTIVE); } else { port = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, if_16550a_type[if_type & 0x0f].iat, if_16550a_type[if_type & 0x0f].iatsz, RF_ACTIVE); } #else port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_COMSIZE, RF_ACTIVE); #endif if (!port) return (ENXIO); #ifdef PC98 if (!IS_8251(if_type)) { if (isa_load_resourcev(port, if_16550a_type[if_type & 0x0f].iat, if_16550a_type[if_type & 0x0f].iatsz) != 0) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return ENXIO; } } #endif iobase = rman_get_start(port); unit = device_get_unit(dev); com = device_get_softc(dev); flags = device_get_flags(dev); if (unit >= sio_numunits) sio_numunits = unit + 1; #ifdef PC98 obufsize = 256; if (if_type == COM_IF_RSA98III) obufsize = 2048; if ((obuf = malloc(obufsize * 2, M_DEVBUF, M_NOWAIT)) == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return ENXIO; } bzero(obuf, obufsize * 2); #endif /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->ioportres = port; com->ioportrid = rid; com->bst = rman_get_bustag(port); com->bsh = rman_get_bushandle(port); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(flags) != 0; com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; com->tx_fifo_size = 1; #ifdef PC98 com->obufsize = obufsize; com->obuf1 = obuf; com->obuf2 = obuf + obufsize; #endif com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; #ifdef PC98 com->pc98_if_type = if_type; if (IS_8251(if_type)) { pc98_set_ioport(com); if (if_type == COM_IF_INTERNAL && pc98_check_8251fifo()) { com->pc98_8251fifo = 1; com->pc98_8251fifo_enable = 0; } } else { bus_addr_t *iat = if_16550a_type[if_type & 0x0f].iat; com->data_port = iobase + iat[com_data]; com->int_ctl_port = iobase + iat[com_ier]; com->int_id_port = iobase + iat[com_iir]; com->modem_ctl_port = iobase + iat[com_mcr]; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + iat[com_lsr]; com->modem_status_port = iobase + iat[com_msr]; } #else /* not PC98 */ com->data_port = iobase + com_data; com->int_ctl_port = iobase + com_ier; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; #endif #ifdef PC98 if (!IS_8251(if_type) && rclk == 0) rclk = if_16550a_type[if_type & 0x0f].rclk; #else if (rclk == 0) rclk = DEFAULT_RCLK; #endif com->rclk = rclk; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { #ifdef PC98 if (IS_8251(com->pc98_if_type)) DELAY(100000); #endif com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; com->lt_out.c_ispeed = com->lt_out.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; if (siosetwater(com, com->it_in.c_ispeed) != 0) { mtx_unlock_spin(&sio_lock); /* * Leave i/o resources allocated if this is a `cn'-level * console, so that other devices can't snarf them. */ if (iobase != siocniobase) bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return (ENOMEM); } mtx_unlock_spin(&sio_lock); termioschars(&com->it_in); com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifndef PC98 if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags) && !COM_NOSCR(flags)) { u_char scr; u_char scr1; u_char scr2; scr = sio_getreg(com, com_scr); sio_setreg(com, com_scr, 0xa5); scr1 = sio_getreg(com, com_scr); sio_setreg(com, com_scr, 0x5a); scr2 = sio_getreg(com, com_scr); sio_setreg(com, com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250 or not responding"); goto determined_type; } } #endif /* !PC98 */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (com->pc98_8251fifo && !COM_NOFIFO(flags)) com->tx_fifo_size = 16; com_int_TxRx_disable( com ); com_cflag_and_speed_set( com, com->it_in.c_cflag, comdefaultrate ); com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); com_send_break_off( com ); if (com->pc98_if_type == COM_IF_INTERNAL) { printf(" (internal%s%s)", com->pc98_8251fifo ? " fifo" : "", PC98SIO_baud_rate_port(com->pc98_if_type) != -1 ? " v-fast" : ""); } else { printf(" 8251%s", if_8251_type[com->pc98_if_type & 0x0f].name); } } else { #endif /* PC98 */ sio_setreg(com, com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: if (COM_NOFIFO(flags)) { printf(" 16550A fifo disabled"); break; } com->hasfifo = TRUE; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) { com->tx_fifo_size = 2048; com->rsabase = iobase; outb(com->rsabase + rsa_ier, 0x00); outb(com->rsabase + rsa_frr, 0x00); } #else if (COM_ST16650A(flags)) { printf(" ST16650A"); com->st16650a = TRUE; com->tx_fifo_size = 32; break; } if (COM_TI16754(flags)) { printf(" TI16754"); com->tx_fifo_size = 64; break; } #endif printf(" 16550A"); #ifdef COM_ESP #ifdef PC98 if (com->pc98_if_type == COM_IF_ESP98) #endif for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(com, *espp)) { com->tx_fifo_size = 1024; break; } if (com->esp) break; #endif #ifdef PC98 com->tx_fifo_size = 16; #else com->tx_fifo_size = COM_FIFOSIZE(flags); if (com->tx_fifo_size == 0) com->tx_fifo_size = 16; else printf(" lookalike with %u bytes FIFO", com->tx_fifo_size); #endif break; } #ifdef PC98 if (com->pc98_if_type == COM_IF_RSB3000) { /* Set RSB-2000/3000 Extended Buffer mode. */ u_char lcr; lcr = sio_getreg(com, com_cfcr); sio_setreg(com, com_cfcr, lcr | CFCR_DLAB); sio_setreg(com, com_emr, EMR_EXBUFF | EMR_EFMODE); sio_setreg(com, com_cfcr, lcr); } #endif #ifdef COM_ESP if (com->esp) { /* * Set 16550 compatibility mode. * We don't use the ESP_MODE_SCALE bit to increase the * fifo trigger levels because we can't handle large * bursts of input. * XXX flow control should be set in comparam(), not here. */ #ifdef PC98 outb(com->esp_port + ESP98_CMD1, ESP_SETMODE); outb(com->esp_port + ESP98_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); #else outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); #endif /* Set RTS/CTS flow control. */ #ifdef PC98 outb(com->esp_port + ESP98_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP98_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP98_CMD2, ESP_FLOW_CTS); #else outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); #endif /* Set flow-control levels. */ #ifdef PC98 outb(com->esp_port + ESP98_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP98_CMD2, HIBYTE(768)); outb(com->esp_port + ESP98_CMD2, LOBYTE(768)); outb(com->esp_port + ESP98_CMD2, HIBYTE(512)); outb(com->esp_port + ESP98_CMD2, LOBYTE(512)); #else outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); #endif #ifdef PC98 /* Set UART clock prescaler. */ outb(com->esp_port + ESP98_CMD1, ESP_SETCLOCK); outb(com->esp_port + ESP98_CMD2, 2); /* 4 times */ #endif } #endif /* COM_ESP */ sio_setreg(com, com_fifo, 0); #ifdef PC98 printf("%s", if_16550a_type[com->pc98_if_type & 0x0f].name); #else determined_type: ; #endif #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { device_t masterdev; com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(flags)) printf(" master"); printf(")"); masterdev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); com->no_irq = (masterdev == NULL || bus_get_resource(masterdev, SYS_RES_IRQ, 0, NULL, NULL) != 0); } #endif /* COM_MULTIPORT */ #ifdef PC98 } #endif if (unit == comconsole) printf(", console"); if (COM_IIR_TXRDYBUG(flags)) printf(" with a buggy IIR_TXRDY implementation"); printf("\n"); if (sio_fast_ih == NULL) { swi_add(&tty_ithd, "tty:sio", siopoll, NULL, SWI_TTY, 0, &sio_fast_ih); swi_add(&clk_ithd, "tty:sio", siopoll, NULL, SWI_TTY, 0, &sio_slow_ih); } minorbase = UNIT_TO_MINOR(unit); com->devs[0] = make_dev(&sio_cdevsw, minorbase, UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); com->devs[1] = make_dev(&sio_cdevsw, minorbase | CONTROL_INIT_STATE, UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); com->devs[2] = make_dev(&sio_cdevsw, minorbase | CONTROL_LOCK_STATE, UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); com->devs[3] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK, UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); com->devs[4] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_INIT_STATE, UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); com->devs[5] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_LOCK_STATE, UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); for (rid = 0; rid < 6; rid++) com->devs[rid]->si_drv1 = com; com->flags = flags; com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; if (COM_PPSCTS(flags)) com->pps_bit = MSR_CTS; else com->pps_bit = MSR_DCD; pps_init(&com->pps); rid = 0; com->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (com->irqres) { ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, INTR_TYPE_TTY | INTR_FAST, siointr, com, &com->cookie); if (ret) { ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, INTR_TYPE_TTY, siointr, com, &com->cookie); if (ret == 0) device_printf(dev, "unable to activate interrupt in fast mode - using normal mode\n"); } if (ret) device_printf(dev, "could not activate interrupt\n"); #if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ defined(ALT_BREAK_TO_DEBUGGER)) /* * Enable interrupts for early break-to-debugger support * on the console. */ if (ret == 0 && unit == comconsole) outb(siocniobase + com_ier, IER_ERXRDY | IER_ERLS | IER_EMSC); #endif } return (0); } static int sioopen(dev, flag, mode, td) dev_t dev; int flag; int mode; struct thread *td; { struct com_s *com; int error; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); if (com == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); tp = dev->si_tty = com->tp = ttymalloc(com->tp); s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && suser(td)) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_stop = comstop; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; #ifdef PC98 if (!IS_8251(com->pc98_if_type)) #endif (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; #ifdef PC98 if (IS_8251(com->pc98_if_type)) { com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS); pc98_msrint_start(dev); if (com->pc98_8251fifo) { com->pc98_8251fifo_enable = 1; outb(I8251F_fcr, CTRL8251F_ENABLE | CTRL8251F_XMT_RST | CTRL8251F_RCV_RST); } } #endif /* * XXX we should goto open_top if comparam() slept. */ if (com->hasfifo) { int i; /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ for (i = 0; i < 500; i++) { sio_setreg(com, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) outb(com->rsabase + rsa_frr , 0x00); #endif /* * XXX the delays are for superstitious * historical reasons. It must be less than * the character time at the maximum * supported speed (87 usec at 115200 bps * 8N1). Otherwise we might loop endlessly * if data is streaming in. We used to use * delays of 100. That usually worked * because DELAY(100) used to usually delay * for about 85 usec instead of 100. */ DELAY(50); #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III ? !(inb(com->rsabase + rsa_srr) & 0x08) : !(inb(com->line_status_port) & LSR_RXRDY)) break; #else if (!(inb(com->line_status_port) & LSR_RXRDY)) break; #endif sio_setreg(com, com_fifo, 0); DELAY(50); (void) inb(com->data_port); } if (i == 500) { error = EIO; goto out; } } mtx_lock_spin(&sio_lock); #ifdef PC98 if (IS_8251(com->pc98_if_type)) { com_tiocm_bis(com, TIOCM_LE); com->pc98_prev_modem_status = pc98_get_modem_status(com); com_int_Rx_enable(com); } else { #endif (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(com->int_ctl_port, IER_ERXRDY | IER_ERLS | IER_EMSC | (COM_IIR_TXRDYBUG(com->flags) ? 0 : IER_ETXRDY)); #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) { outb(com->rsabase + rsa_ier, 0x1d); outb(com->int_ctl_port, IER_ERLS | IER_EMSC); } #endif #ifdef PC98 } #endif mtx_unlock_spin(&sio_lock); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ #ifdef PC98 if ((IS_8251(com->pc98_if_type) && (pc98_get_modem_status(com) & TIOCM_CAR)) || (!IS_8251(com->pc98_if_type) && (com->prev_modem_status & MSR_DCD)) || mynor & CALLOUT_MASK) ttyld_modem(tp, 1); #else if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) ttyld_modem(tp, 1); #endif } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = ttyld_open(tp, dev); com->hotchar = ttyldoptim(tp); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, td) dev_t dev; int flag; int mode; struct thread *td; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL) return (ENODEV); tp = com->tp; s = spltty(); ttyld_close(tp, flag); #ifdef PC98 com->modem_checking = 0; #endif com->hotchar = ttyldoptim(tp); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); if (com->ibuf != NULL) free(com->ibuf, M_DEVBUF); bzero(tp, sizeof *tp); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { int s; struct tty *tp; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = FALSE; com->do_dcd_timestamp = FALSE; com->pps.ppsparam.mode = 0; #ifdef PC98 if (IS_8251(com->pc98_if_type)) com_send_break_off(com); else #endif sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); tp = com->tp; #if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ defined(ALT_BREAK_TO_DEBUGGER)) /* * Leave interrupts enabled and don't clear DTR if this is the * console. This allows us to detect break-to-debugger events * while the console device is closed. */ if (com->unit != comconsole) #endif { #ifdef PC98 int tmp; if (IS_8251(com->pc98_if_type)) com_int_TxRx_disable(com); else sio_setreg(com, com_ier, 0); if (com->pc98_if_type == COM_IF_RSA98III) outb(com->rsabase + rsa_ier, 0x00); if (IS_8251(com->pc98_if_type)) tmp = pc98_get_modem_status(com) & TIOCM_CAR; else tmp = com->prev_modem_status & MSR_DCD; #else sio_setreg(com, com_ier, 0); #endif if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || (!com->active_out #ifdef PC98 && !(tmp) #else && !(com->prev_modem_status & MSR_DCD) #endif && !(com->it_in.c_cflag & CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { #ifdef PC98 if (IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); else #endif (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } #ifdef PC98 else { if (IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_LE); } #endif } #ifdef PC98 if (com->pc98_8251fifo) { if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_XMT_RST | CTRL8251F_RCV_RST); com->pc98_8251fifo_enable = 0; } #endif if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ sio_setreg(com, com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct com_s *com; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL || com->gone) return (ENODEV); return (ttyld_read(com->tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct com_s *com; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); if (com == NULL || com->gone) return (ENODEV); /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return (ttyld_write(com->tp, uio, flag)); } static void siobusycheck(chan) void *chan; { struct com_s *com; int s; com = (struct com_s *)chan; /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. * If siointr1() clears CS_BUSY after we look at it, then we'll get * called again. Reading the line status port outside of siointr1() * is safe because CS_BUSY is clear so there are no output interrupts * to lose. */ s = spltty(); if (com->state & CS_BUSY) com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ #ifdef PC98 else if ((IS_8251(com->pc98_if_type) && ((com->pc98_8251fifo_enable && (inb(I8251F_lsr) & (STS8251F_TxRDY | STS8251F_TxEMP)) == (STS8251F_TxRDY | STS8251F_TxEMP)) || (!com->pc98_8251fifo_enable && (inb(com->sts_port) & (STS8251_TxRDY | STS8251_TxEMP)) == (STS8251_TxRDY | STS8251_TxEMP)))) || ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY))) { #else else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY)) { #endif com->tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); com->extra_state &= ~CSE_BUSYCHECK; } else timeout(siobusycheck, com, hz / 100); splx(s); } static u_int siodivisor(rclk, speed) u_long rclk; speed_t speed; { long actual_speed; u_int divisor; int error; if (speed == 0) return (0); #if UINT_MAX > (ULONG_MAX - 1) / 8 if (speed > (ULONG_MAX - 1) / 8) return (0); #endif divisor = (rclk / (8UL * speed) + 1) / 2; if (divisor == 0 || divisor >= 65536) return (0); actual_speed = rclk / (16UL * divisor); /* 10 times error in percent: */ error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2; /* 3.0% maximum error tolerance: */ if (error < -30 || error > 30) return (0); return (divisor); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } /* * Call this function with the sio_lock mutex held. It will return with the * lock still held. */ static void sioinput(com) struct com_s *com; { u_char *buf; int incc; u_char line_status; int recv_data; struct tty *tp; buf = com->ibuf; tp = com->tp; if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; return; } if (tp->t_state & TS_CAN_BYPASS_L_RINT) { /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ do { /* * This may look odd, but it is using save-and-enable * semantics instead of the save-and-disable semantics * that are used everywhere else. */ mtx_unlock_spin(&sio_lock); incc = com->iptr - buf; if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); buf += incc; tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } mtx_lock_spin(&sio_lock); } while (buf < com->iptr); } else { do { /* * This may look odd, but it is using save-and-enable * semantics instead of the save-and-disable semantics * that are used everywhere else. */ mtx_unlock_spin(&sio_lock); line_status = buf[com->ierroff]; recv_data = *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } ttyld_rint(tp, recv_data); mtx_lock_spin(&sio_lock); } while (buf < com->iptr); } com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; /* * There is now room for another low-level buffer full of input, * so enable RTS if it is now disabled and there is room in the * high-level buffer. */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if ((com->state & CS_RTS_IFLOW) && !(com_tiocm_get(com) & TIOCM_RTS) && !(tp->t_state & TS_TBLOCK)) com_tiocm_bis(com, TIOCM_RTS); } else { if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } #else if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #endif } static void siointr(arg) void *arg; { struct com_s *com; #if defined(PC98) && defined(COM_MULTIPORT) u_char rsa_buf_status; #endif #ifndef COM_MULTIPORT com = (struct com_s *)arg; mtx_lock_spin(&sio_lock); siointr1(com); mtx_unlock_spin(&sio_lock); #else /* COM_MULTIPORT */ bool_t possibly_more_intrs; int unit; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ mtx_lock_spin(&sio_lock); do { possibly_more_intrs = FALSE; for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); /* * XXX COM_LOCK(); * would it work here, or be counter-productive? */ #ifdef PC98 if (com != NULL && !com->gone && IS_8251(com->pc98_if_type)) { siointr1(com); } else if (com != NULL && !com->gone && com->pc98_if_type == COM_IF_RSA98III) { rsa_buf_status = inb(com->rsabase + rsa_srr) & 0xc9; if ((rsa_buf_status & 0xc8) || !(rsa_buf_status & 0x01)) { siointr1(com); if (rsa_buf_status != (inb(com->rsabase + rsa_srr) & 0xc9)) possibly_more_intrs = TRUE; } } else #endif if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); mtx_unlock_spin(&sio_lock); #endif /* COM_MULTIPORT */ } static struct timespec siots[8]; static int siotso; static int volatile siotsunit = -1; static int sysctl_siots(SYSCTL_HANDLER_ARGS) { char buf[128]; long long delta; size_t len; int error, i, tso; for (i = 1, tso = siotso; i < tso; i++) { delta = (long long)(siots[i].tv_sec - siots[i - 1].tv_sec) * 1000000000 + (siots[i].tv_nsec - siots[i - 1].tv_nsec); len = sprintf(buf, "%lld\n", delta); if (delta >= 110000) len += sprintf(buf + len - 1, ": *** %ld.%09ld\n", (long)siots[i].tv_sec, siots[i].tv_nsec) - 1; if (i == tso - 1) buf[len - 1] = '\0'; error = SYSCTL_OUT(req, buf, len); if (error != 0) return (error); uio_yield(); } return (0); } SYSCTL_PROC(_machdep, OID_AUTO, siots, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_siots, "A", "sio timestamps"); static void siointr1(com) struct com_s *com; { u_char int_ctl; u_char int_ctl_new; u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; #ifdef PC98 u_char tmp = 0; u_char rsa_buf_status = 0; int rsa_tx_fifo_size = 0; #endif /* PC98 */ if (COM_IIR_TXRDYBUG(com->flags)) { int_ctl = inb(com->int_ctl_port); int_ctl_new = int_ctl; } else { int_ctl = 0; int_ctl_new = 0; } while (!com->gone) { #ifdef PC98 status_read:; if (IS_8251(com->pc98_if_type)) { if (com->pc98_8251fifo_enable) tmp = inb(I8251F_lsr); else tmp = inb(com->sts_port); more_intr: line_status = 0; if (com->pc98_8251fifo_enable) { if (tmp & STS8251F_TxRDY) line_status |= LSR_TXRDY; if (tmp & STS8251F_RxRDY) line_status |= LSR_RXRDY; if (tmp & STS8251F_TxEMP) line_status |= LSR_TSRE; if (tmp & STS8251F_PE) line_status |= LSR_PE; if (tmp & STS8251F_OE) line_status |= LSR_OE; if (tmp & STS8251F_BD_SD) line_status |= LSR_BI; } else { if (tmp & STS8251_TxRDY) line_status |= LSR_TXRDY; if (tmp & STS8251_RxRDY) line_status |= LSR_RXRDY; if (tmp & STS8251_TxEMP) line_status |= LSR_TSRE; if (tmp & STS8251_PE) line_status |= LSR_PE; if (tmp & STS8251_OE) line_status |= LSR_OE; if (tmp & STS8251_FE) line_status |= LSR_FE; if (tmp & STS8251_BD_SD) line_status |= LSR_BI; } } else { #endif /* PC98 */ if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) { modem_status = inb(com->modem_status_port); if ((modem_status ^ com->last_modem_status) & com->pps_bit) { pps_capture(&com->pps); pps_event(&com->pps, (modem_status & com->pps_bit) ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); } } line_status = inb(com->line_status_port); #ifdef PC98 } if (com->pc98_if_type == COM_IF_RSA98III) rsa_buf_status = inb(com->rsabase + rsa_srr); #endif /* PC98 */ /* input event? (check first to help avoid overruns) */ #ifndef PC98 while (line_status & LSR_RCV_MASK) { #else while ((line_status & LSR_RCV_MASK) || (com->pc98_if_type == COM_IF_RSA98III && (rsa_buf_status & 0x08))) { #endif /* PC98 */ /* break/unnattached error bits or real input? */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (com->pc98_8251fifo_enable) { recv_data = inb(I8251F_data); if (tmp & (STS8251F_PE | STS8251F_OE | STS8251F_BD_SD)) { pc98_i8251_or_cmd(com, CMD8251_ER); recv_data = 0; } } else { recv_data = inb(com->data_port); if (tmp & (STS8251_PE | STS8251_OE | STS8251_FE | STS8251_BD_SD)) { pc98_i8251_or_cmd(com, CMD8251_ER); recv_data = 0; } } } else if (com->pc98_if_type == COM_IF_RSA98III) { if (!(rsa_buf_status & 0x08)) recv_data = 0; else recv_data = inb(com->data_port); } else #endif if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); #ifdef DDB #ifdef ALT_BREAK_TO_DEBUGGER if (com->unit == comconsole && db_alt_break(recv_data, &com->alt_brk_state) != 0) breakpoint(); #endif /* ALT_BREAK_TO_DEBUGGER */ #endif /* DDB */ if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { /* * Don't store BI if IGNBRK or FE/PE if IGNPAR. * Otherwise, push the work to a higher level * (to handle PARMRK) if we're bypassing. * Otherwise, convert BI/FE and PE+INPCK to 0. * * This makes bypassing work right in the * usual "raw" case (IGNBRK set, and IGNPAR * and INPCK clear). * * Note: BI together with FE/PE means just BI. */ if (line_status & LSR_BI) { #if defined(DDB) && defined(BREAK_TO_DEBUGGER) if (com->unit == comconsole) { breakpoint(); goto cont; } #endif if (com->tp == NULL || com->tp->t_iflag & IGNBRK) goto cont; } else { if (com->tp == NULL || com->tp->t_iflag & IGNPAR) goto cont; } if (com->tp->t_state & TS_CAN_BYPASS_L_RINT && (line_status & (LSR_BI | LSR_FE) || com->tp->t_iflag & INPCK)) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) swi_sched(sio_fast_ih, 0); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; swi_sched(sio_slow_ih, SWI_DELAY); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) swi_sched(sio_fast_ih, 0); #endif ioptr[0] = recv_data; ioptr[com->ierroff] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) #ifdef PC98 IS_8251(com->pc98_if_type) ? com_tiocm_bic(com, TIOCM_RTS) : #endif outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) goto txrdy; /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) goto status_read; else #endif line_status = inb(com->line_status_port) & 0x7F; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) rsa_buf_status = inb(com->rsabase + rsa_srr); #endif /* PC98 */ } /* modem status change? (always check before doing output) */ #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { #endif modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; swi_sched(sio_fast_ih, 0); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } #ifdef PC98 } #endif txrdy: /* output queued and everything ready? */ #ifndef PC98 if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { #else if (((com->pc98_if_type == COM_IF_RSA98III) ? (rsa_buf_status & 0x02) : (line_status & LSR_TXRDY)) && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { #endif #ifdef PC98 Port_t tmp_data_port; if (IS_8251(com->pc98_if_type) && com->pc98_8251fifo_enable) tmp_data_port = I8251F_data; else tmp_data_port = com->data_port; #endif ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1 && com->unit != siotsunit) { u_int ocount; ocount = com->obufq.l_tail - ioptr; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) { rsa_buf_status = inb(com->rsabase + rsa_srr); rsa_tx_fifo_size = 1024; if (!(rsa_buf_status & 0x01)) rsa_tx_fifo_size = 2048; if (ocount > rsa_tx_fifo_size) ocount = rsa_tx_fifo_size; } else #endif if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do #ifdef PC98 outb(tmp_data_port, *ioptr++); #else outb(com->data_port, *ioptr++); #endif while (--ocount != 0); } else { #ifdef PC98 outb(tmp_data_port, *ioptr++); #else outb(com->data_port, *ioptr++); #endif ++com->bytes_out; if (com->unit == siotsunit && siotso < sizeof siots / sizeof siots[0]) nanouptime(&siots[siotso++]); } #ifdef PC98 if (IS_8251(com->pc98_if_type)) if (!(pc98_check_i8251_interrupt(com) & IEN_TxFLAG)) com_int_Tx_enable(com); #endif com->obufq.l_head = ioptr; if (COM_IIR_TXRDYBUG(com->flags)) int_ctl_new = int_ctl | IER_ETXRDY; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ if (COM_IIR_TXRDYBUG(com->flags)) int_ctl_new = int_ctl & ~IER_ETXRDY; com->state &= ~CS_BUSY; #if defined(PC98) if (IS_8251(com->pc98_if_type) && pc98_check_i8251_interrupt(com) & IEN_TxFLAG) com_int_Tx_disable(com); #endif } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; /* handle at high level ASAP */ swi_sched(sio_fast_ih, 0); } } #ifdef PC98 if (COM_IIR_TXRDYBUG(com->flags) && int_ctl != int_ctl_new) { if (com->pc98_if_type == COM_IF_RSA98III) { int_ctl_new &= ~(IER_ETXRDY | IER_ERXRDY); outb(com->int_ctl_port, int_ctl_new); outb(com->rsabase + rsa_ier, 0x1d); } else outb(com->int_ctl_port, int_ctl_new); } #else if (COM_IIR_TXRDYBUG(com->flags) && int_ctl != int_ctl_new) outb(com->int_ctl_port, int_ctl_new); #endif } #ifdef PC98 else if (line_status & LSR_TXRDY) { if (IS_8251(com->pc98_if_type)) if (pc98_check_i8251_interrupt(com) & IEN_TxFLAG) com_int_Tx_disable(com); } if (IS_8251(com->pc98_if_type)) { if (com->pc98_8251fifo_enable) { if ((tmp = inb(I8251F_lsr)) & STS8251F_RxRDY) goto more_intr; } else { if ((tmp = inb(com->sts_port)) & STS8251_RxRDY) goto more_intr; } } #endif /* finished? */ #ifndef COM_MULTIPORT #ifdef PC98 if (IS_8251(com->pc98_if_type)) return; #endif if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, td) dev_t dev; u_long cmd; caddr_t data; int flag; struct thread *td; { struct com_s *com; int error; int mynor; int s; struct tty *tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL || com->gone) return (ENODEV); if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(td); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } tp = com->tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = ttyioctl(dev, cmd, data, flag, td); com->hotchar = ttyldoptim(tp); if (error != ENOTTY) return (error); s = spltty(); #ifdef PC98 if (IS_8251(com->pc98_if_type)) { switch (cmd) { case TIOCSBRK: com_send_break_on(com); break; case TIOCCBRK: com_send_break_off(com); break; case TIOCSDTR: com_tiocm_bis(com, TIOCM_DTR | TIOCM_RTS); break; case TIOCCDTR: com_tiocm_bic(com, TIOCM_DTR); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: com_tiocm_set(com, *(int *)data); break; case TIOCMBIS: com_tiocm_bis(com, *(int *)data); break; case TIOCMBIC: com_tiocm_bic(com, *(int *)data); break; case TIOCMGET: *(int *)data = com_tiocm_get(com); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(td); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); error = pps_ioctl(cmd, data, &com->pps); if (error == ENODEV) error = ENOTTY; return (error); } } else { #endif switch (cmd) { case TIOCSBRK: sio_setreg(com, com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(td); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); error = pps_ioctl(cmd, data, &com->pps); if (error == ENODEV) error = ENOTTY; return (error); } #ifdef PC98 } #endif splx(s); return (0); } /* software interrupt handler for SWI_TTY */ static void siopoll(void *dummy) { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < sio_numunits; ++unit) { struct com_s *com; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL || com->gone) { /* * Discard any events related to never-opened or * going-away devices. */ mtx_lock_spin(&sio_lock); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; mtx_unlock_spin(&sio_lock); continue; } if (com->iptr != com->ibuf) { mtx_lock_spin(&sio_lock); sioinput(com); mtx_unlock_spin(&sio_lock); } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { #endif mtx_lock_spin(&sio_lock); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; mtx_unlock_spin(&sio_lock); if (delta_modem_status & MSR_DCD) ttyld_modem(tp, com->prev_modem_status & MSR_DCD); #ifdef PC98 } #endif } if (com->state & CS_ODONE) { mtx_lock_spin(&sio_lock); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; mtx_unlock_spin(&sio_lock); if (!(com->state & CS_BUSY) && !(com->extra_state & CSE_BUSYCHECK)) { timeout(siobusycheck, com, hz / 100); com->extra_state |= CSE_BUSYCHECK; } ttyld_start(tp); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; u_int divisor; u_char dlbh; u_char dlbl; u_char efr_flowbits; int s; int unit; #ifdef PC98 u_char param = 0; #endif unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); if (com == NULL) return (ENODEV); #ifdef PC98 cfcr = 0; if (IS_8251(com->pc98_if_type)) { if (pc98_ttspeedtab(com, t->c_ospeed, &divisor) != 0) return (EINVAL); } else { #endif /* check requested parameters */ if (t->c_ispeed != (t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed)) return (EINVAL); divisor = siodivisor(com->rclk, t->c_ispeed); if (divisor == 0) return (EINVAL); #ifdef PC98 } #endif /* parameters are OK, convert them to the com struct and the device */ s = spltty(); #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (t->c_ospeed == 0) com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); else com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); } else #endif if (t->c_ospeed == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); cflag = t->c_cflag; #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { #endif switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. * * The fifo trigger level cannot be set at RX_HIGH for high * speed connections without further work on reducing * interrupt disablement times in other parts of the system, * without producing silo overflow errors. */ com->fifo_image = com->unit == siotsunit ? 0 : t->c_ispeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_MEDH; #ifdef COM_ESP /* * The Hayes ESP card needs the fifo DMA mode bit set * in compatibility mode. If not, it will interrupt * for each character received. */ if (com->esp) com->fifo_image |= FIFO_DMA_MODE; #endif sio_setreg(com, com_fifo, com->fifo_image); } #ifdef PC98 } #endif /* * This returns with interrupts disabled so that we can complete * the speed change atomically. Keeping interrupts disabled is * especially important while com_data is hidden. */ (void) siosetwater(com, t->c_ispeed); #ifdef PC98 if (IS_8251(com->pc98_if_type)) com_cflag_and_speed_set(com, cflag, t->c_ospeed); else { #endif sio_setreg(com, com_cfcr, cfcr | CFCR_DLAB); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (UMC8669F), setting them while input * is arriving loses sync until data stops arriving. */ dlbl = divisor & 0xFF; if (sio_getreg(com, com_dlbl) != dlbl) sio_setreg(com, com_dlbl, dlbl); dlbh = divisor >> 8; if (sio_getreg(com, com_dlbh) != dlbh) sio_setreg(com, com_dlbh, dlbh); #ifdef PC98 } #endif efr_flowbits = 0; if (cflag & CRTS_IFLOW) { com->state |= CS_RTS_IFLOW; efr_flowbits |= EFR_AUTORTS; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) com_tiocm_bis(com, TIOCM_RTS); else outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #endif } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) { param = inb(com->rsabase + rsa_msr); outb(com->rsabase + rsa_msr, param & 0x14); } #endif if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; efr_flowbits |= EFR_AUTOCTS; #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (!(pc98_get_modem_status(com) & TIOCM_CTS)) com->state &= ~CS_ODEVREADY; } else if (com->pc98_if_type == COM_IF_RSA98III) { /* Set automatic flow control mode */ outb(com->rsabase + rsa_msr, param | 0x08); } else #endif if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } #ifdef PC98 if (!IS_8251(com->pc98_if_type)) sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); #else if (com->st16650a) { sio_setreg(com, com_lcr, LCR_EFR_ENABLE); sio_setreg(com, com_efr, (sio_getreg(com, com_efr) & ~(EFR_AUTOCTS | EFR_AUTORTS)) | efr_flowbits); } sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); #endif /* XXX shouldn't call functions while intrs are disabled. */ com->hotchar = ttyldoptim(tp); mtx_unlock_spin(&sio_lock); splx(s); comstart(tp); if (com->ibufold != NULL) { free(com->ibufold, M_DEVBUF); com->ibufold = NULL; } return (0); } /* * This function must be called with the sio_lock mutex released and will * return with it obtained. */ static int siosetwater(com, speed) struct com_s *com; speed_t speed; { int cp4ticks; u_char *ibuf; int ibufsize; struct tty *tp; /* * Make the buffer size large enough to handle a softtty interrupt * latency of about 2 ticks without loss of throughput or data * (about 3 ticks if input flow control is not used or not honoured, * but a bit less for CS5-CS7 modes). */ cp4ticks = speed / 10 / hz * 4; for (ibufsize = 128; ibufsize < cp4ticks;) ibufsize <<= 1; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) ibufsize = 2048; #endif if (ibufsize == com->ibufsize) { mtx_lock_spin(&sio_lock); return (0); } /* * Allocate input buffer. The extra factor of 2 in the size is * to allow for an error byte for each input byte. */ ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); if (ibuf == NULL) { mtx_lock_spin(&sio_lock); return (ENOMEM); } /* Initialize non-critical variables. */ com->ibufold = com->ibuf; com->ibufsize = ibufsize; tp = com->tp; if (tp != NULL) { tp->t_ififosize = 2 * ibufsize; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; } /* * Read current input buffer, if any. Continue with interrupts * disabled. */ mtx_lock_spin(&sio_lock); if (com->iptr != com->ibuf) sioinput(com); /*- * Initialize critical variables, including input buffer watermarks. * The external device is asked to stop sending when the buffer * exactly reaches high water, or when the high level requests it. * The high level is notified immediately (rather than at a later * clock tick) when this watermark is reached. * The buffer size is chosen so the watermark should almost never * be reached. * The low watermark is invisibly 0 since the buffer is always * emptied all at once. */ com->iptr = com->ibuf = ibuf; com->ibufend = ibuf + ibufsize; com->ierroff = ibufsize; com->ihighwater = ibuf + 3 * ibufsize / 4; return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); if (com == NULL) return; s = spltty(); mtx_lock_spin(&sio_lock); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if ((com_tiocm_get(com) & TIOCM_RTS) && (com->state & CS_RTS_IFLOW)) com_tiocm_bic(com, TIOCM_RTS); } else { if ((com->mcr_image & MCR_RTS) && (com->state & CS_RTS_IFLOW)) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } #else if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #endif } else { #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (!(com_tiocm_get(com) & TIOCM_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) com_tiocm_bis(com, TIOCM_RTS); } else { if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } #else if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #endif } mtx_unlock_spin(&sio_lock); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, #ifdef PC98 com->obufsize); #else sizeof com->obuf1); #endif com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; mtx_lock_spin(&sio_lock); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } mtx_unlock_spin(&sio_lock); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, #ifdef PC98 com->obufsize); #else sizeof com->obuf2); #endif com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; mtx_lock_spin(&sio_lock); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } mtx_unlock_spin(&sio_lock); } tp->t_state |= TS_BUSY; } mtx_lock_spin(&sio_lock); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ mtx_unlock_spin(&sio_lock); ttwwakeup(tp); splx(s); } static void comstop(tp, rw) struct tty *tp; int rw; { struct com_s *com; #ifdef PC98 int rsa98_tmp = 0; #endif com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com == NULL || com->gone) return; mtx_lock_spin(&sio_lock); if (rw & FWRITE) { #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { #endif if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif sio_setreg(com, com_fifo, FIFO_XMT_RST | com->fifo_image); #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) for (rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++) sio_setreg(com, com_fifo, FIFO_XMT_RST | com->fifo_image); } #endif com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { if (com->pc98_if_type == COM_IF_RSA98III) for (rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++) sio_getreg(com, com_data); #endif if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif sio_setreg(com, com_fifo, FIFO_RCV_RST | com->fifo_image); #ifdef PC98 } #endif com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } mtx_unlock_spin(&sio_lock); comstart(tp); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); mtx_lock_spin(&sio_lock); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } mtx_unlock_spin(&sio_lock); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { mtx_lock_spin(&sio_lock); siointr1(com); mtx_unlock_spin(&sio_lock); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < sio_numunits; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; mtx_lock_spin(&sio_lock); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; mtx_unlock_spin(&sio_lock); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } #ifdef PC98 /* commint is called when modem control line changes */ static void commint(dev_t dev) { register struct tty *tp; int stat,delta; struct com_s *com; int mynor,unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); tp = com->tp; stat = com_tiocm_get(com); delta = com_tiocm_get_delta(com); if (com->state & CS_CTS_OFLOW) { if (stat & TIOCM_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } if ((delta & TIOCM_CAR) && (mynor & CALLOUT_MASK) == 0) { if (stat & TIOCM_CAR ) (void)ttyld_modem(tp, 1); else if (ttyld_modem(tp, 0) == 0) { /* negate DTR, RTS */ com_tiocm_bic(com, (tp->t_cflag & HUPCL) ? TIOCM_DTR|TIOCM_RTS|TIOCM_LE : TIOCM_LE ); /* disable IENABLE */ com_int_TxRx_disable( com ); } } } #endif /* * Following are all routines needed for SIO to act as console */ struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; /* * This is a function in order to not replicate "ttyd%d" more * places than absolutely necessary. */ static void siocnset(struct consdev *cd, int unit) { cd->cn_unit = unit; sprintf(cd->cn_name, "ttyd%d", unit); } #ifndef __alpha__ static speed_t siocngetspeed(Port_t, u_long rclk); #endif static void siocnclose(struct siocnstate *sp, Port_t iobase); static void siocnopen(struct siocnstate *sp, Port_t iobase, int speed); static void siocntxwait(Port_t iobase); #ifdef __alpha__ int siocnattach(int port, int speed); int siogdbattach(int port, int speed); int siogdbgetc(void); void siogdbputc(int c); #else static cn_probe_t siocnprobe; static cn_init_t siocninit; static cn_term_t siocnterm; #endif static cn_checkc_t siocncheckc; static cn_getc_t siocngetc; static cn_putc_t siocnputc; #ifndef __alpha__ CONS_DRIVER(sio, siocnprobe, siocninit, siocnterm, siocngetc, siocncheckc, siocnputc, NULL); #endif #if DDB > 0 static struct consdev gdbconsdev; #endif static void siocntxwait(iobase) Port_t iobase; { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } #ifndef __alpha__ /* * Read the serial port specified and try to figure out what speed * it's currently running at. We're assuming the serial port has * been initialized and is basicly idle. This routine is only intended * to be run at system startup. * * If the value read from the serial port doesn't make sense, return 0. */ static speed_t siocngetspeed(iobase, rclk) Port_t iobase; u_long rclk; { u_int divisor; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(iobase + com_dlbl); dlbh = inb(iobase + com_dlbh); outb(iobase + com_cfcr, cfcr); divisor = dlbh << 8 | dlbl; /* XXX there should be more sanity checking. */ if (divisor == 0) return (CONSPEED); return (rclk / (16UL * divisor)); } #endif static void siocnopen(sp, iobase, speed) struct siocnstate *sp; Port_t iobase; int speed; { u_int divisor; u_char dlbh; u_char dlbl; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(iobase); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (Startech), setting them clears the * data input register. This also reduces the effects of the * UMC8669F bug. */ divisor = siodivisor(comdefaultrclk, speed); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp, iobase) struct siocnstate *sp; Port_t iobase; { /* * Restore the device control registers. */ siocntxwait(iobase); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); if (sp->dlbl != inb(iobase + com_dlbl)) outb(iobase + com_dlbl, sp->dlbl); if (sp->dlbh != inb(iobase + com_dlbh)) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } #ifndef __alpha__ static void siocnprobe(cp) struct consdev *cp; { speed_t boot_speed; u_char cfcr; u_int divisor; int s, unit; struct siocnstate sp; /* * Find our first enabled console, if any. If it is a high-level * console device, then initialize it and return successfully. * If it is a low-level console device, then initialize it and * return unsuccessfully. It must be initialized in both cases * for early use by console drivers and debuggers. Initializing * the hardware is not necessary in all cases, since the i/o * routines initialize it on the fly, but it is necessary if * input might arrive while the hardware is switched back to an * uninitialized state. We can't handle multiple console devices * yet because our low-level routines don't take a device arg. * We trust the user to set the console flags properly so that we * don't need to probe. */ cp->cn_pri = CN_DEAD; for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ int flags; if (resource_disabled("sio", unit)) continue; if (resource_int_value("sio", unit, "flags", &flags)) continue; if (COM_CONSOLE(flags) || COM_DEBUGGER(flags)) { int port; Port_t iobase; if (resource_int_value("sio", unit, "port", &port)) continue; iobase = port; s = spltty(); if (boothowto & RB_SERIAL) { boot_speed = siocngetspeed(iobase, comdefaultrclk); if (boot_speed) comdefaultrate = boot_speed; } /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, comdefaultrate); outb(iobase + com_dlbl, divisor & 0xff); outb(iobase + com_dlbh, divisor >> 8); outb(iobase + com_cfcr, cfcr); siocnopen(&sp, iobase, comdefaultrate); splx(s); if (COM_CONSOLE(flags) && !COM_LLCONSOLE(flags)) { siocnset(cp, unit); cp->cn_pri = COM_FORCECONSOLE(flags) || boothowto & RB_SERIAL ? CN_REMOTE : CN_NORMAL; siocniobase = iobase; siocnunit = unit; } if (COM_DEBUGGER(flags)) { printf("sio%d: gdb debugging port\n", unit); siogdbiobase = iobase; siogdbunit = unit; #if DDB > 0 siocnset(&gdbconsdev, unit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; #endif } } } #ifdef __i386__ #if DDB > 0 /* * XXX Ugly Compatability. * If no gdb port has been specified, set it to be the console * as some configuration files don't specify the gdb port. */ if (gdb_arg == NULL && (boothowto & RB_GDB)) { printf("Warning: no GDB port specified. Defaulting to sio%d.\n", siocnunit); printf("Set flag 0x80 on desired GDB port in your\n"); printf("configuration file (currently sio only).\n"); siogdbiobase = siocniobase; siogdbunit = siocnunit; siocnset(&gdbconsdev, siocnunit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; } #endif #endif } static void siocninit(cp) struct consdev *cp; { comconsole = cp->cn_unit; } static void siocnterm(cp) struct consdev *cp; { comconsole = -1; } #endif #ifdef __alpha__ CONS_DRIVER(sio, NULL, NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL); int siocnattach(port, speed) int port; int speed; { int s; u_char cfcr; u_int divisor; struct siocnstate sp; int unit = 0; /* XXX random value! */ siocniobase = port; siocnunit = unit; comdefaultrate = speed; sio_consdev.cn_pri = CN_NORMAL; siocnset(&sio_consdev, unit); s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, comdefaultrate); outb(siocniobase + com_dlbl, divisor & 0xff); outb(siocniobase + com_dlbh, divisor >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp, siocniobase, comdefaultrate); splx(s); cnadd(&sio_consdev); return (0); } int siogdbattach(port, speed) int port; int speed; { int s; u_char cfcr; u_int divisor; struct siocnstate sp; int unit = 1; /* XXX random value! */ siogdbiobase = port; gdbdefaultrate = speed; printf("sio%d: gdb debugging port\n", unit); siogdbunit = unit; #if DDB > 0 siocnset(&gdbconsdev, unit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; #endif s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siogdbiobase + com_cfcr); outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, gdbdefaultrate); outb(siogdbiobase + com_dlbl, divisor & 0xff); outb(siogdbiobase + com_dlbh, divisor >> 8); outb(siogdbiobase + com_cfcr, cfcr); siocnopen(&sp, siogdbiobase, gdbdefaultrate); splx(s); return (0); } #endif static int siocncheckc(struct consdev *cd) { int c; Port_t iobase; int s; struct siocnstate sp; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = -1; siocnclose(&sp, iobase); splx(s); return (c); } static int siocngetc(struct consdev *cd) { int c; Port_t iobase; int s; struct siocnstate sp; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } static void siocnputc(struct consdev *cd, int c) { int need_unlock; int s; struct siocnstate sp; Port_t iobase; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); need_unlock = 0; if (sio_inited == 2 && !mtx_owned(&sio_lock)) { mtx_lock_spin(&sio_lock); need_unlock = 1; } siocnopen(&sp, iobase, speed); siocntxwait(iobase); outb(iobase + com_data, c); siocnclose(&sp, iobase); if (need_unlock) mtx_unlock_spin(&sio_lock); splx(s); } #ifdef __alpha__ int siogdbgetc() { int c; Port_t iobase; speed_t speed; int s; struct siocnstate sp; if (siogdbunit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } void siogdbputc(c) int c; { Port_t iobase; speed_t speed; int s; struct siocnstate sp; if (siogdbunit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); siocntxwait(siogdbiobase); outb(siogdbiobase + com_data, c); siocnclose(&sp, siogdbiobase); splx(s); } #endif #ifdef PC98 /* * pc98 local function */ static void com_tiocm_set(struct com_s *com, int msr) { int s; int tmp = 0; int mask = CMD8251_TxEN|CMD8251_RxEN|CMD8251_DTR|CMD8251_RTS; s=spltty(); com->pc98_prev_modem_status = ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ) | ( com->pc98_prev_modem_status & ~(TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); tmp |= (CMD8251_TxEN|CMD8251_RxEN); if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_clear_or_cmd( com, mask, tmp ); splx(s); } static void com_tiocm_bis(struct com_s *com, int msr) { int s; int tmp = 0; s=spltty(); com->pc98_prev_modem_status |= ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); tmp |= CMD8251_TxEN|CMD8251_RxEN; if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_or_cmd( com, tmp ); splx(s); } static void com_tiocm_bic(struct com_s *com, int msr) { int s; int tmp = msr; s=spltty(); com->pc98_prev_modem_status &= ~( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_clear_cmd( com, tmp ); splx(s); } static int com_tiocm_get(struct com_s *com) { return( com->pc98_prev_modem_status ); } static int com_tiocm_get_delta(struct com_s *com) { int tmp; tmp = com->pc98_modem_delta; com->pc98_modem_delta = 0; return( tmp ); } /* convert to TIOCM_?? ( ioctl.h ) */ static int pc98_get_modem_status(struct com_s *com) { register int msr; msr = com->pc98_prev_modem_status & ~(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); if (com->pc98_8251fifo_enable) { int stat2; stat2 = inb(I8251F_msr); if ( stat2 & CICSCDF_CD ) msr |= TIOCM_CAR; if ( stat2 & CICSCDF_CI ) msr |= TIOCM_RI; if ( stat2 & CICSCDF_DR ) msr |= TIOCM_DSR; if ( stat2 & CICSCDF_CS ) msr |= TIOCM_CTS; #if COM_CARRIER_DETECT_EMULATE if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { msr |= TIOCM_CAR; } #endif } else { int stat, stat2; stat = inb(com->sts_port); stat2 = inb(com->in_modem_port); if ( !(stat2 & CICSCD_CD) ) msr |= TIOCM_CAR; if ( !(stat2 & CICSCD_CI) ) msr |= TIOCM_RI; if ( stat & STS8251_DSR ) msr |= TIOCM_DSR; if ( !(stat2 & CICSCD_CS) ) msr |= TIOCM_CTS; #if COM_CARRIER_DETECT_EMULATE if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { msr |= TIOCM_CAR; } #endif } return(msr); } static void pc98_check_msr(void* chan) { int msr, delta; int s; register struct tty *tp; struct com_s *com; int mynor; int unit; dev_t dev; dev=(dev_t)chan; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); tp = com->tp; s = spltty(); msr = pc98_get_modem_status(com); /* make change flag */ delta = msr ^ com->pc98_prev_modem_status; if ( delta & TIOCM_CAR ) { if ( com->modem_car_chg_timer ) { if ( -- com->modem_car_chg_timer ) msr ^= TIOCM_CAR; } else { if ((com->modem_car_chg_timer = (msr & TIOCM_CAR) ? DCD_ON_RECOGNITION : DCD_OFF_TOLERANCE) != 0) msr ^= TIOCM_CAR; } } else com->modem_car_chg_timer = 0; delta = ( msr ^ com->pc98_prev_modem_status ) & (TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); com->pc98_prev_modem_status = msr; delta = ( com->pc98_modem_delta |= delta ); splx(s); if ( com->modem_checking || (tp->t_state & (TS_ISOPEN)) ) { if ( delta ) { commint(dev); } timeout(pc98_check_msr, (caddr_t)dev, PC98_CHECK_MODEM_INTERVAL); } else { com->modem_checking = 0; } } static void pc98_msrint_start(dev_t dev) { struct com_s *com; int mynor; int unit; int s = spltty(); mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); /* modem control line check routine envoke interval is 1/10 sec */ if ( com->modem_checking == 0 ) { com->pc98_prev_modem_status = pc98_get_modem_status(com); com->pc98_modem_delta = 0; timeout(pc98_check_msr, (caddr_t)dev, PC98_CHECK_MODEM_INTERVAL); com->modem_checking = 1; } splx(s); } static void pc98_disable_i8251_interrupt(struct com_s *com, int mod) { /* disable interrupt */ register int tmp; mod |= ~(IEN_Tx|IEN_TxEMP|IEN_Rx); COM_INT_DISABLE tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); outb( com->intr_ctrl_port, (com->intr_enable&=~mod) | tmp ); COM_INT_ENABLE } static void pc98_enable_i8251_interrupt(struct com_s *com, int mod) { register int tmp; COM_INT_DISABLE tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); outb( com->intr_ctrl_port, (com->intr_enable|=mod) | tmp ); COM_INT_ENABLE } static int pc98_check_i8251_interrupt(struct com_s *com) { return ( com->intr_enable & 0x07 ); } static void pc98_i8251_clear_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE tmp = com->pc98_prev_siocmd & ~(x); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE); COM_INT_ENABLE } static void pc98_i8251_or_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); tmp = com->pc98_prev_siocmd | (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE); COM_INT_ENABLE } static void pc98_i8251_set_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); tmp = (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE); COM_INT_ENABLE } static void pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x) { int tmp; COM_INT_DISABLE if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); tmp = com->pc98_prev_siocmd & ~(clr); tmp |= (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE); COM_INT_ENABLE } static int pc98_i8251_get_cmd(struct com_s *com) { return com->pc98_prev_siocmd; } static int pc98_i8251_get_mod(struct com_s *com) { return com->pc98_prev_siomod; } static void pc98_i8251_reset(struct com_s *com, int mode, int command) { if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, CMD8251_RESET); /* internal reset */ DELAY(2); outb(com->cmd_port, mode ); /* mode register */ com->pc98_prev_siomod = mode; DELAY(2); pc98_i8251_set_cmd( com, (command|CMD8251_ER) ); DELAY(10); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE | CTRL8251F_XMT_RST | CTRL8251F_RCV_RST); } static void pc98_check_sysclock(void) { /* get system clock from port */ if ( pc98_machine_type & M_8M ) { /* 8 MHz system & H98 */ sysclock = 8; } else { /* 5 MHz system */ sysclock = 5; } } static void com_cflag_and_speed_set( struct com_s *com, int cflag, int speed) { int cfcr=0; int previnterrupt; u_int count; if (pc98_ttspeedtab(com, speed, &count) != 0) return; previnterrupt = pc98_check_i8251_interrupt(com); pc98_disable_i8251_interrupt( com, IEN_Tx|IEN_TxEMP|IEN_Rx ); switch ( cflag&CSIZE ) { case CS5: cfcr = MOD8251_5BITS; break; case CS6: cfcr = MOD8251_6BITS; break; case CS7: cfcr = MOD8251_7BITS; break; case CS8: cfcr = MOD8251_8BITS; break; } if ( cflag&PARENB ) { if ( cflag&PARODD ) cfcr |= MOD8251_PODD; else cfcr |= MOD8251_PEVEN; } else cfcr |= MOD8251_PDISAB; if ( cflag&CSTOPB ) cfcr |= MOD8251_STOP2; else cfcr |= MOD8251_STOP1; if ( count & 0x10000 ) cfcr |= MOD8251_CLKX1; else cfcr |= MOD8251_CLKX16; if (epson_machine_id != 0x20) { /* XXX */ int tmp; while (!((tmp = inb(com->sts_port)) & STS8251_TxEMP)) ; } /* set baud rate from ospeed */ pc98_set_baud_rate( com, count ); if ( cfcr != pc98_i8251_get_mod(com) ) pc98_i8251_reset(com, cfcr, pc98_i8251_get_cmd(com) ); pc98_enable_i8251_interrupt( com, previnterrupt ); } static int pc98_ttspeedtab(struct com_s *com, int speed, u_int *divisor) { int if_type, effect_sp, count = -1, mod; if_type = com->pc98_if_type & 0x0f; switch (com->pc98_if_type) { case COM_IF_INTERNAL: if (PC98SIO_baud_rate_port(if_type) != -1) { count = ttspeedtab(speed, if_8251_type[if_type].speedtab); if (count > 0) { count |= COM1_EXT_CLOCK; break; } } /* for *1CLK asynchronous! mode, TEFUTEFU */ mod = (sysclock == 5) ? 2457600 : 1996800; effect_sp = ttspeedtab( speed, pc98speedtab ); if ( effect_sp < 0 ) /* XXX */ effect_sp = ttspeedtab( (speed - 1), pc98speedtab ); if ( effect_sp <= 0 ) return effect_sp; if ( effect_sp == speed ) mod /= 16; if ( mod % effect_sp ) return(-1); count = mod / effect_sp; if ( count > 65535 ) return(-1); if ( effect_sp != speed ) count |= 0x10000; break; case COM_IF_PC9861K_1: case COM_IF_PC9861K_2: count = 1; break; case COM_IF_IND_SS_1: case COM_IF_IND_SS_2: case COM_IF_PIO9032B_1: case COM_IF_PIO9032B_2: count = ttspeedtab( speed, if_8251_type[if_type].speedtab ); break; case COM_IF_B98_01_1: case COM_IF_B98_01_2: count = ttspeedtab( speed, if_8251_type[if_type].speedtab ); #ifdef B98_01_OLD if (count == 0 || count == 1) { count += 4; count |= 0x20000; /* x1 mode for 76800 and 153600 */ } #endif break; } if (count < 0) return count; *divisor = (u_int) count; return 0; } static void pc98_set_baud_rate( struct com_s *com, u_int count ) { int if_type, io, s; if_type = com->pc98_if_type & 0x0f; io = rman_get_start(com->ioportres) & 0xff00; switch (com->pc98_if_type) { case COM_IF_INTERNAL: if (PC98SIO_baud_rate_port(if_type) != -1) { if (count & COM1_EXT_CLOCK) { outb((Port_t)PC98SIO_baud_rate_port(if_type), count & 0xff); break; } else { outb((Port_t)PC98SIO_baud_rate_port(if_type), 0x09); } } if (count == 0) return; /* set i8253 */ s = splclock(); if (count != 3) outb( 0x77, 0xb6 ); else outb( 0x77, 0xb4 ); outb( 0x5f, 0); outb( 0x75, count & 0xff ); outb( 0x5f, 0); outb( 0x75, (count >> 8) & 0xff ); splx(s); break; case COM_IF_IND_SS_1: case COM_IF_IND_SS_2: outb(io | PC98SIO_intr_ctrl_port(if_type), 0); outb(io | PC98SIO_baud_rate_port(if_type), 0); outb(io | PC98SIO_baud_rate_port(if_type), 0xc0); outb(io | PC98SIO_baud_rate_port(if_type), (count >> 8) | 0x80); outb(io | PC98SIO_baud_rate_port(if_type), count & 0xff); break; case COM_IF_PIO9032B_1: case COM_IF_PIO9032B_2: outb(io | PC98SIO_baud_rate_port(if_type), count); break; case COM_IF_B98_01_1: case COM_IF_B98_01_2: outb(io | PC98SIO_baud_rate_port(if_type), count & 0x0f); #ifdef B98_01_OLD /* * Some old B98_01 board should be controlled * in different way, but this hasn't been tested yet. */ outb(io | PC98SIO_func_port(if_type), (count & 0x20000) ? 0xf0 : 0xf2); #endif break; } } static int pc98_check_if_type(device_t dev, struct siodev *iod) { int irr, io, if_type, tmp; static short irq_tab[2][8] = { { 3, 5, 6, 9, 10, 12, 13, -1}, { 3, 10, 12, 13, 5, 6, 9, -1} }; if_type = iod->if_type & 0x0f; iod->irq = 0; io = isa_get_port(dev) & 0xff00; if (IS_8251(iod->if_type)) { if (PC98SIO_func_port(if_type) != -1) { outb(io | PC98SIO_func_port(if_type), 0xf2); tmp = ttspeedtab(9600, if_8251_type[if_type].speedtab); if (tmp != -1 && PC98SIO_baud_rate_port(if_type) != -1) outb(io | PC98SIO_baud_rate_port(if_type), tmp); } iod->cmd = io | PC98SIO_cmd_port(if_type); iod->sts = io | PC98SIO_sts_port(if_type); iod->mod = io | PC98SIO_in_modem_port(if_type); iod->ctrl = io | PC98SIO_intr_ctrl_port(if_type); if (iod->if_type == COM_IF_INTERNAL) { iod->irq = 4; if (pc98_check_8251vfast()) { PC98SIO_baud_rate_port(if_type) = I8251F_div; if_8251_type[if_type].speedtab = pc98fast_speedtab; } } else { tmp = inb( iod->mod ) & if_8251_type[if_type].irr_mask; if ((isa_get_port(dev) & 0xff) == IO_COM2) iod->irq = irq_tab[0][tmp]; else iod->irq = irq_tab[1][tmp]; } } else { irr = if_16550a_type[if_type].irr_read; #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(device_get_flags(dev)) || device_get_unit(dev) == COM_MPMASTER(device_get_flags(dev))) #endif if (irr != -1) { tmp = inb(io | irr); if (isa_get_port(dev) & 0x01) /* XXX depend on RSB-384 */ iod->irq = irq_tab[1][tmp >> 3]; else iod->irq = irq_tab[0][tmp & 0x07]; } } if ( iod->irq == -1 ) return -1; return 0; } static void pc98_set_ioport(struct com_s *com) { int if_type = com->pc98_if_type & 0x0f; Port_t io = rman_get_start(com->ioportres) & 0xff00; pc98_check_sysclock(); com->data_port = io | PC98SIO_data_port(if_type); com->cmd_port = io | PC98SIO_cmd_port(if_type); com->sts_port = io | PC98SIO_sts_port(if_type); com->in_modem_port = io | PC98SIO_in_modem_port(if_type); com->intr_ctrl_port = io | PC98SIO_intr_ctrl_port(if_type); } static int pc98_check_8251vfast(void) { int i; outb(I8251F_div, 0x8c); DELAY(10); for (i = 0; i < 100; i++) { if ((inb(I8251F_div) & 0x80) != 0) { i = 0; break; } DELAY(1); } outb(I8251F_div, 0); DELAY(10); for (; i < 100; i++) { if ((inb(I8251F_div) & 0x80) == 0) return 1; DELAY(1); } return 0; } static int pc98_check_8251fifo(void) { u_char tmp1, tmp2; tmp1 = inb(I8251F_iir); DELAY(10); tmp2 = inb(I8251F_iir); if (((tmp1 ^ tmp2) & 0x40) != 0 && ((tmp1 | tmp2) & 0x20) == 0) return 1; return 0; } #endif /* PC98 defined */ Index: head/sys/pc98/i386/machdep.c =================================================================== --- head/sys/pc98/i386/machdep.c (revision 130343) +++ head/sys/pc98/i386/machdep.c (revision 130344) @@ -1,2855 +1,2855 @@ /*- * 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. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_compat.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_isa.h" #include "opt_kstack_pages.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_npx.h" #include "opt_perfmon.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #include #endif #include #include #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #include #ifdef PERFMON #include #endif #ifdef SMP #include #include #endif #ifdef DEV_ISA #include #endif #ifdef PC98 #include #include #else #include #endif #include #include #include /* Sanity check for __curthread() */ CTASSERT(offsetof(struct pcpu, pc_curthread) == 0); extern void init386(int first); extern void dblfault_handler(void); extern void printcpuinfo(void); /* XXX header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) #if !defined(CPU_ENABLE_SSE) && defined(I686_CPU) #define CPU_ENABLE_SSE #endif #if defined(CPU_DISABLE_SSE) #undef CPU_ENABLE_SSE #endif static void cpu_startup(void *); static void fpstate_drop(struct thread *td); static void get_fpcontext(struct thread *td, mcontext_t *mcp); static int set_fpcontext(struct thread *td, const mcontext_t *mcp); #ifdef CPU_ENABLE_SSE static void set_fpregs_xmm(struct save87 *, struct savexmm *); static void fill_fpregs_xmm(struct savexmm *, struct save87 *); #endif /* CPU_ENABLE_SSE */ SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) #ifdef PC98 int need_pre_dma_flush; /* If 1, use wbinvd befor DMA transfer. */ int need_post_dma_flush; /* If 1, use invd after DMA transfer. */ #endif int _udatasel, _ucodesel; u_int basemem; #ifdef PC98 static int ispc98 = 1; #else static int ispc98 = 0; #endif SYSCTL_INT(_machdep, OID_AUTO, ispc98, CTLFLAG_RD, &ispc98, 0, ""); int cold = 1; #ifdef COMPAT_43 static void osendsig(sig_t catcher, int sig, sigset_t *mask, u_long code); #endif #ifdef COMPAT_FREEBSD4 static void freebsd4_sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code); #endif long Maxmem = 0; #ifdef PC98 int Maxmem_under16M = 0; #endif vm_paddr_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) struct kva_md_info kmi; static struct trapframe proc0_tf; #ifndef SMP static struct pcpu __pcpu; #endif struct mtx icu_lock; static void cpu_startup(dummy) void *dummy; { /* * Good {morning,afternoon,evening,night}. */ startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %ju (%ju MB)\n", ptoa((uintmax_t)Maxmem), ptoa((uintmax_t)Maxmem) / 1048576); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { vm_paddr_t size; size = phys_avail[indx + 1] - phys_avail[indx]; printf( "0x%016jx - 0x%016jx, %ju bytes (%ju pages)\n", (uintmax_t)phys_avail[indx], (uintmax_t)phys_avail[indx + 1] - 1, (uintmax_t)size, (uintmax_t)size / PAGE_SIZE); } } vm_ksubmap_init(&kmi); printf("avail memory = %ju (%ju MB)\n", ptoa((uintmax_t)cnt.v_free_count), ptoa((uintmax_t)cnt.v_free_count) / 1048576); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); cpu_setregs(); } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top 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. */ #ifdef COMPAT_43 static void osendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct osigframe sf, *fp; struct proc *p; struct thread *td; struct sigacts *psp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct osigframe *)(td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct osigframe)); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else fp = (struct osigframe *)regs->tf_esp - 1; /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_arg2 = (register_t)&fp->sf_siginfo; sf.sf_siginfo.si_signo = sig; sf.sf_siginfo.si_code = code; sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; } else { /* Old FreeBSD-style arguments. */ sf.sf_arg2 = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* Save most if not all of trap frame. */ sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; sf.sf_siginfo.si_sc.sc_es = regs->tf_es; sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; sf.sf_siginfo.si_sc.sc_gs = rgs(); sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; /* Build the signal context to be used by osigreturn(). */ sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; sf.sf_siginfo.si_sc.sc_err = regs->tf_err; /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_siginfo.si_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* See sendsig() for comments. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(*fp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - szosigcode; regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; load_gs(_udatasel); regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } #endif /* COMPAT_43 */ #ifdef COMPAT_FREEBSD4 static void freebsd4_sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe4 sf, *sfp; struct proc *p; struct thread *td; struct sigacts *psp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe4 *)(td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct sigframe4)); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe4 *)regs->tf_esp - 1; /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - szfreebsd4_sigcode; regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } #endif /* COMPAT_FREEBSD4 */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe sf, *sfp; struct proc *p; struct thread *td; struct sigacts *psp; char *sp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); #ifdef COMPAT_FREEBSD4 if (SIGISMEMBER(psp->ps_freebsd4, sig)) { freebsd4_sendsig(catcher, sig, mask, code); return; } #endif #ifdef COMPAT_43 if (SIGISMEMBER(psp->ps_osigset, sig)) { osendsig(catcher, sig, mask, code); return; } #endif regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext); /* magic */ get_fpcontext(td, &sf.sf_uc.uc_mcontext); fpstate_drop(td); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sp = td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct sigframe); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sp = (char *)regs->tf_esp - sizeof(struct sigframe); /* Align to 16 bytes. */ sfp = (struct sigframe *)((unsigned int)sp & ~0xF); /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } /* * Build siginfo_t for SA thread */ void cpu_thread_siginfo(int sig, u_long code, siginfo_t *si) { struct proc *p; struct thread *td; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); bzero(si, sizeof(*si)); si->si_signo = sig; si->si_code = code; si->si_addr = (void *)td->td_frame->tf_err; /* XXXKSE fill other fields */ } /* * 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 * state to gain improper privileges. * * MPSAFE */ #ifdef COMPAT_43 int osigreturn(td, uap) struct thread *td; struct osigreturn_args /* { struct osigcontext *sigcntxp; } */ *uap; { struct osigcontext sc; struct trapframe *regs; struct osigcontext *scp; struct proc *p = td->td_proc; int eflags, error; regs = td->td_frame; error = copyin(uap->sigcntxp, &sc, sizeof(sc)); if (error != 0) return (error); scp = ≻ eflags = scp->sc_ps; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ if (!CS_SECURE(scp->sc_cs)) { trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; regs->tf_fs = scp->sc_fs; } /* Restore remaining registers. */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (scp->sc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif SIGSETOLD(td->td_sigmask, scp->sc_mask); SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } #endif /* COMPAT_43 */ #ifdef COMPAT_FREEBSD4 /* * MPSAFE */ int freebsd4_sigreturn(td, uap) struct thread *td; struct freebsd4_sigreturn_args /* { const ucontext4 *sigcntxp; } */ *uap; { struct ucontext4 uc; struct proc *p = td->td_proc; struct trapframe *regs; const struct ucontext4 *ucp; int cs, eflags, error; error = copyin(uap->sigcntxp, &uc, sizeof(uc)); if (error != 0) return (error); ucp = &uc; regs = td->td_frame; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("freebsd4_sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("freebsd4_sigreturn: cs = 0x%x\n", cs); trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (ucp->uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = ucp->uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } #endif /* COMPAT_FREEBSD4 */ /* * MPSAFE */ int sigreturn(td, uap) struct thread *td; struct sigreturn_args /* { const __ucontext *sigcntxp; } */ *uap; { ucontext_t uc; struct proc *p = td->td_proc; struct trapframe *regs; const ucontext_t *ucp; int cs, eflags, error, ret; error = copyin(uap->sigcntxp, &uc, sizeof(uc)); if (error != 0) return (error); ucp = &uc; regs = td->td_frame; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("sigreturn: cs = 0x%x\n", cs); trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } ret = set_fpcontext(td, &ucp->uc_mcontext); if (ret != 0) return (ret); bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (ucp->uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = ucp->uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Hook to idle the CPU when possible. In the SMP case we default to * off because a halted cpu will not currently pick up a new thread in the * run queue until the next timer tick. If turned on this will result in * approximately a 4.2% loss in real time performance in buildworld tests * (but improves user and sys times oddly enough), and saves approximately * 5% in power consumption on an idle machine (tests w/2xCPU 1.1GHz P3). * * XXX we need to have a cpu mask of idle cpus and generate an IPI or * otherwise generate some sort of interrupt to wake up cpus sitting in HLT. * Then we can have our cake and eat it too. * * XXX I'm turning it on for SMP as well by default for now. It seems to * help lock contention somewhat, and this is critical for HTT. -Peter */ static int cpu_idle_hlt = 1; SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW, &cpu_idle_hlt, 0, "Idle loop HLT enable"); static void cpu_idle_default(void) { /* * we must absolutely guarentee that hlt is the * absolute next instruction after sti or we * introduce a timing window. */ __asm __volatile("sti; hlt"); } /* * Note that we have to be careful here to avoid a race between checking * sched_runnable() and actually halting. If we don't do this, we may waste * the time between calling hlt and the next interrupt even though there * is a runnable process. */ void cpu_idle(void) { #ifdef SMP if (mp_grab_cpu_hlt()) return; #endif if (cpu_idle_hlt) { disable_intr(); if (sched_runnable()) enable_intr(); else (*cpu_idle_hook)(); } } /* Other subsystems (e.g., ACPI) can hook this later. */ void (*cpu_idle_hook)(void) = cpu_idle_default; /* * Clear registers on exec */ void exec_setregs(td, entry, stack, ps_strings) struct thread *td; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = td->td_frame; struct pcb *pcb = td->td_pcb; /* Reset pc->pcb_gs and %gs before possibly invalidating it. */ pcb->pcb_gs = _udatasel; load_gs(_udatasel); if (td->td_proc->p_md.md_ldt) user_ldt_free(td); bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* * Reset the hardware debug registers if they were in use. * They won't have any meaning for the newly exec'd process. */ if (pcb->pcb_flags & PCB_DBREGS) { pcb->pcb_dr0 = 0; pcb->pcb_dr1 = 0; pcb->pcb_dr2 = 0; pcb->pcb_dr3 = 0; pcb->pcb_dr6 = 0; pcb->pcb_dr7 = 0; if (pcb == PCPU_GET(curpcb)) { /* * Clear the debug registers on the running * CPU, otherwise they will end up affecting * the next process we switch to. */ reset_dbregs(); } pcb->pcb_flags &= ~PCB_DBREGS; } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ td->td_pcb->pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); /* Initialize the npx (if any) for the current process. */ /* * XXX the above load_cr0() also initializes it and is a layering * violation if NPX is configured. It drops the npx partially * and this would be fatal if we were interrupted now, and decided * to force the state to the pcb, and checked the invariant * (CR0_TS clear) if and only if PCPU_GET(fpcurthread) != NULL). * ALL of this can happen except the check. The check used to * happen and be fatal later when we didn't complete the drop * before returning to user mode. This should be fixed properly * soon. */ fpstate_drop(td); /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ td->td_retval[1] = 0; } void cpu_setregs(void) { unsigned int cr0; cr0 = rcr0(); #ifdef SMP cr0 |= CR0_NE; /* Done by npxinit() */ #endif cr0 |= CR0_MP | CR0_TS; /* Done at every execve() too. */ #ifndef I386_CPU cr0 |= CR0_WP | CR0_AM; #endif load_cr0(cr0); load_gs(_udatasel); } static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); u_long bootdev; /* not a dev_t - encoding is different */ SYSCTL_ULONG(_machdep, OID_AUTO, guessed_bootdev, CTLFLAG_RD, &bootdev, 0, "Maybe the Boot device (not in dev_t format)"); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ struct region_descriptor r_gdt, r_idt; /* table descriptors */ int private_tss; /* flag indicating private tss */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0uarea; extern vm_offset_t proc0kstack; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPRIV_SEL 3 SMP Per-Processor Private Data Descriptor */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 4 Proc 0 Tss Descriptor */ { 0x0, /* segment base address */ sizeof(struct i386tss)-1,/* length */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 5 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 6 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 7 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSLOWMEM_SEL 8 BIOS access to realmode segment 0x40, must be #8 in GDT */ { 0x400, /* segment base address */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 9 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE32_SEL 10 BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE16_SEL 11 BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSDATA_SEL 12 BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSUTIL_SEL 13 BIOS 16-bit interface (Utility) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSARGS_SEL 14 BIOS 16-bit interface (Arguments) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(xmm), IDTVEC(lcall_syscall), IDTVEC(int0x80_syscall); #ifdef DDB /* * Display the index and function name of any IDT entries that don't use * the default 'rsvd' entry point. */ DB_SHOW_COMMAND(idt, db_show_idt) { struct gate_descriptor *ip; int idx, quit; uintptr_t func; ip = idt; db_setup_paging(db_simple_pager, &quit, DB_LINES_PER_PAGE); for (idx = 0, quit = 0; idx < NIDT; idx++) { func = (ip->gd_hioffset << 16 | ip->gd_looffset); if (func != (uintptr_t)&IDTVEC(rsvd)) { db_printf("%3d\t", idx); db_printsym(func, DB_STGY_PROC); db_printf("\n"); } ip++; } } #endif void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } #define PHYSMAP_SIZE (2 * 8) /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. * * XXX first should be vm_paddr_t. */ static void getmemsize(int first) { #ifdef PC98 int i, physmap_idx, pa_indx, pg_n; u_int extmem, under16; vm_offset_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t *pte; char *cp; #else int i, physmap_idx, pa_indx; u_int extmem; struct vm86frame vmf; struct vm86context vmc; vm_paddr_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t *pte; char *cp; struct bios_smap *smap; #endif #ifdef PC98 /* XXX - some of EPSON machines can't use PG_N */ pg_n = PG_N; if (pc98_machine_type & M_EPSON_PC98) { switch (epson_machine_id) { #ifdef WB_CACHE default: #endif case 0x34: /* PC-486HX */ case 0x35: /* PC-486HG */ case 0x3B: /* PC-486HA */ pg_n = 0; break; } } bzero(physmap, sizeof(physmap)); /* * Perform "base memory" related probes & setup */ under16 = pc98_getmemsize(&basemem, &extmem); if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) pmap_kenter(KERNBASE + pa, pa); /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ pte = (pt_entry_t *)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; #else /* PC98 */ bzero(&vmf, sizeof(struct vm86frame)); bzero(physmap, sizeof(physmap)); basemem = 0; /* * map page 1 R/W into the kernel page table so we can use it * as a buffer. The kernel will unmap this page later. */ pmap_kenter(KERNBASE + (1 << PAGE_SHIFT), 1 << PAGE_SHIFT); /* * get memory map with INT 15:E820 */ vmc.npages = 0; smap = (void *)vm86_addpage(&vmc, 1, KERNBASE + (1 << PAGE_SHIFT)); vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di); physmap_idx = 0; vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = sizeof(struct bios_smap); i = vm86_datacall(0x15, &vmf, &vmc); if (i || vmf.vmf_eax != SMAP_SIG) break; if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%016llx len=%016llx\n", smap->type, smap->base, smap->length); if (smap->type != 0x01) goto next_run; if (smap->length == 0) goto next_run; if (smap->base >= 0xffffffff) { printf("%uK of memory above 4GB ignored\n", (u_int)(smap->length / 1024)); goto next_run; } for (i = 0; i <= physmap_idx; i += 2) { if (smap->base < physmap[i + 1]) { if (boothowto & RB_VERBOSE) printf( "Overlapping or non-montonic memory region, ignoring second region\n"); goto next_run; } } if (smap->base == physmap[physmap_idx + 1]) { physmap[physmap_idx + 1] += smap->length; goto next_run; } physmap_idx += 2; if (physmap_idx == PHYSMAP_SIZE) { printf( "Too many segments in the physical address map, giving up\n"); break; } physmap[physmap_idx] = smap->base; physmap[physmap_idx + 1] = smap->base + smap->length; next_run: ; } while (vmf.vmf_ebx != 0); /* * Perform "base memory" related probes & setup */ for (i = 0; i <= physmap_idx; i += 2) { if (physmap[i] == 0x00000000) { basemem = physmap[i + 1] / 1024; break; } } /* Fall back to the old compatibility function for base memory */ if (basemem == 0) { vm86_intcall(0x12, &vmf); basemem = vmf.vmf_ax; } if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) pmap_kenter(KERNBASE + pa, pa); /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ pte = (pt_entry_t *)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; if (physmap[1] != 0) goto physmap_done; /* * If we failed above, try memory map with INT 15:E801 */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { extmem = vmf.vmf_cx + vmf.vmf_dx * 64; } else { #if 0 vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); extmem = vmf.vmf_ax; #else /* * Prefer the RTC value for extended memory. */ extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8); #endif } /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. * * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((extmem > 15 * 1024) && (extmem < 16 * 1024)) extmem = 15 * 1024; #endif /* PC98 */ physmap[0] = 0; physmap[1] = basemem * 1024; physmap_idx = 2; physmap[physmap_idx] = 0x100000; physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; #ifdef PC98 if ((under16 != 16 * 1024) && (extmem > 15 * 1024)) { /* 15M - 16M region is cut off, so need to divide chunk */ physmap[physmap_idx + 1] = under16 * 1024; physmap_idx += 2; physmap[physmap_idx] = 0x1000000; physmap[physmap_idx + 1] = physmap[2] + extmem * 1024; } #else physmap_done: #endif /* * Now, physmap contains a map of physical memory. */ #ifdef SMP /* make hole for AP bootstrap code */ physmap[1] = mp_bootaddress(physmap[1]); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif /* * hw.physmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ if ((cp = getenv("hw.physmem")) != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(cp, &ep, 0); if ((ep != cp) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", cp); else Maxmem = atop(AllowMem); freeenv(cp); } if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %ldK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa((vm_paddr_t)Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(first, 0); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; pte = CMAP1; /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_paddr_t end; end = ptoa((vm_paddr_t)Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad; int *ptr = (int *)CADDR1; /* * block out kernel memory as not available. */ if (pa >= KERNLOAD && pa < first) continue; page_bad = FALSE; /* * map page into kernel: valid, read/write,non-cacheable */ #ifdef PC98 *pte = pa | PG_V | PG_RW | pg_n; #else *pte = pa | PG_V | PG_RW | PG_N; #endif invltlb(); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) { page_bad = TRUE; } /* * Restore original value. */ *(int *)ptr = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) { continue; } /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf( "Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; } } *pte = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; } void init386(first) int first; { struct gate_descriptor *gdp; int gsel_tss, metadata_missing, off, x; struct pcpu *pc; proc0.p_uarea = proc0uarea; thread0.td_kstack = proc0kstack; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; /* * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ proc_linkup(&proc0, &ksegrp0, &kse0, &thread0); #ifdef PC98 /* * Initialize DMAC */ pc98_init_dmac(); #endif metadata_missing = 0; if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } else { metadata_missing = 1; } if (envmode == 1) kern_envp = static_env; else if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* Init basic tunables, hz etc */ init_param1(); /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = atop(0 - 1); gdt_segs[GDATA_SEL].ssd_limit = atop(0 - 1); #ifdef SMP pc = &SMP_prvspace[0].pcpu; gdt_segs[GPRIV_SEL].ssd_limit = atop(sizeof(struct privatespace) - 1); #else pc = &__pcpu; gdt_segs[GPRIV_SEL].ssd_limit = atop(sizeof(struct pcpu) - 1); #endif gdt_segs[GPRIV_SEL].ssd_base = (int) pc; gdt_segs[GPROC0_SEL].ssd_base = (int) &pc->pc_common_tss; for (x = 0; x < NGDT; x++) ssdtosd(&gdt_segs[x], &gdt[x].sd); r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); pcpu_init(pc, 0, sizeof(struct pcpu)); PCPU_SET(prvspace, pc); PCPU_SET(curthread, &thread0); PCPU_SET(curpcb, thread0.td_pcb); /* * Initialize mutexes. * * icu_lock: in order to allow an interrupt to occur in a critical * section, to set pcpu->ipending (etc...) properly, we * must be able to get the icu lock, so it can't be * under witness. */ mutex_init(); mtx_init(&clock_lock, "clk", NULL, MTX_SPIN); mtx_init(&icu_lock, "icu", NULL, MTX_SPIN | MTX_NOWITNESS); /* make ldt memory segments */ /* * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ ldt_segs[LUCODE_SEL].ssd_limit = atop(VM_MAXUSER_ADDRESS - 1); ldt_segs[LUDATA_SEL].ssd_limit = atop(VM_MAXUSER_ADDRESS - 1); for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DE, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DB, &IDTVEC(dbg), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NMI, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_BP, &IDTVEC(bpt), SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_OF, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_BR, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_UD, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NM, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL , GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DF, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(IDT_FPUGP, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_TS, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NP, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_SS, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_GP, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_PF, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_MF, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_AC, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_MC, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_XF, &IDTVEC(xmm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); /* * Initialize the console before we print anything out. */ cninit(); if (metadata_missing) printf("WARNING: loader(8) metadata is missing!\n"); #ifdef DEV_ISA atpic_startup(); #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(IDT_UD, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_GP, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ /* Note: -16 is so we can grow the trapframe if we came from vm86 */ PCPU_SET(common_tss.tss_esp0, thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE - sizeof(struct pcb) - 16); PCPU_SET(common_tss.tss_ss0, GSEL(GDATA_SEL, SEL_KPL)); gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); private_tss = 0; PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); PCPU_SET(common_tss.tss_ioopt, (sizeof (struct i386tss)) << 16); ltr(gsel_tss); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int)&dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = (int)IdlePTD; dblfault_tss.tss_eip = (int)dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_fs = GSEL(GPRIV_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); vm86_initialize(); getmemsize(first); init_param2(physmem); /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(lcall_syscall); gdp->gd_looffset = x; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = x >> 16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ thread0.td_pcb->pcb_flags = 0; /* XXXKSE */ thread0.td_pcb->pcb_cr3 = (int)IdlePTD; thread0.td_pcb->pcb_ext = 0; thread0.td_frame = &proc0_tf; } void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) { } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL) static void f00f_hack(void *unused) { struct gate_descriptor *new_idt; vm_offset_t tmp; if (!has_f00f_bug) return; GIANT_REQUIRED; printf("Intel Pentium detected, installing workaround for F00F bug\n"); tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); /* Put the problematic entry (#6) at the end of the lower page. */ new_idt = (struct gate_descriptor*) (tmp + PAGE_SIZE - 7 * sizeof(struct gate_descriptor)); bcopy(idt, new_idt, sizeof(idt0)); r_idt.rd_base = (u_int)new_idt; lidt(&r_idt); idt = new_idt; if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(struct thread *td, u_long addr) { td->td_frame->tf_eip = addr; return (0); } int ptrace_single_step(struct thread *td) { td->td_frame->tf_eflags |= PSL_T; return (0); } int fill_regs(struct thread *td, struct reg *regs) { struct pcb *pcb; struct trapframe *tp; tp = td->td_frame; regs->r_fs = tp->tf_fs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = td->td_pcb; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(struct thread *td, struct reg *regs) { struct pcb *pcb; struct trapframe *tp; tp = td->td_frame; if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_fs = regs->r_fs; tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = td->td_pcb; pcb->pcb_gs = regs->r_gs; return (0); } #ifdef CPU_ENABLE_SSE static void fill_fpregs_xmm(sv_xmm, sv_87) struct savexmm *sv_xmm; struct save87 *sv_87; { register struct env87 *penv_87 = &sv_87->sv_env; register struct envxmm *penv_xmm = &sv_xmm->sv_env; int i; bzero(sv_87, sizeof(*sv_87)); /* FPU control/status */ penv_87->en_cw = penv_xmm->en_cw; penv_87->en_sw = penv_xmm->en_sw; penv_87->en_tw = penv_xmm->en_tw; penv_87->en_fip = penv_xmm->en_fip; penv_87->en_fcs = penv_xmm->en_fcs; penv_87->en_opcode = penv_xmm->en_opcode; penv_87->en_foo = penv_xmm->en_foo; penv_87->en_fos = penv_xmm->en_fos; /* FPU registers */ for (i = 0; i < 8; ++i) sv_87->sv_ac[i] = sv_xmm->sv_fp[i].fp_acc; } static void set_fpregs_xmm(sv_87, sv_xmm) struct save87 *sv_87; struct savexmm *sv_xmm; { register struct env87 *penv_87 = &sv_87->sv_env; register struct envxmm *penv_xmm = &sv_xmm->sv_env; int i; /* FPU control/status */ penv_xmm->en_cw = penv_87->en_cw; penv_xmm->en_sw = penv_87->en_sw; penv_xmm->en_tw = penv_87->en_tw; penv_xmm->en_fip = penv_87->en_fip; penv_xmm->en_fcs = penv_87->en_fcs; penv_xmm->en_opcode = penv_87->en_opcode; penv_xmm->en_foo = penv_87->en_foo; penv_xmm->en_fos = penv_87->en_fos; /* FPU registers */ for (i = 0; i < 8; ++i) sv_xmm->sv_fp[i].fp_acc = sv_87->sv_ac[i]; } #endif /* CPU_ENABLE_SSE */ int fill_fpregs(struct thread *td, struct fpreg *fpregs) { #ifdef CPU_ENABLE_SSE if (cpu_fxsr) { fill_fpregs_xmm(&td->td_pcb->pcb_save.sv_xmm, (struct save87 *)fpregs); return (0); } #endif /* CPU_ENABLE_SSE */ bcopy(&td->td_pcb->pcb_save.sv_87, fpregs, sizeof *fpregs); return (0); } int set_fpregs(struct thread *td, struct fpreg *fpregs) { #ifdef CPU_ENABLE_SSE if (cpu_fxsr) { set_fpregs_xmm((struct save87 *)fpregs, &td->td_pcb->pcb_save.sv_xmm); return (0); } #endif /* CPU_ENABLE_SSE */ bcopy(fpregs, &td->td_pcb->pcb_save.sv_87, sizeof *fpregs); return (0); } /* * Get machine context. */ int get_mcontext(struct thread *td, mcontext_t *mcp, int flags) { struct trapframe *tp; tp = td->td_frame; PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(tp->tf_esp); PROC_UNLOCK(curthread->td_proc); mcp->mc_gs = td->td_pcb->pcb_gs; mcp->mc_fs = tp->tf_fs; mcp->mc_es = tp->tf_es; mcp->mc_ds = tp->tf_ds; mcp->mc_edi = tp->tf_edi; mcp->mc_esi = tp->tf_esi; mcp->mc_ebp = tp->tf_ebp; mcp->mc_isp = tp->tf_isp; mcp->mc_ebx = tp->tf_ebx; if (flags & GET_MC_CLEAR_RET) { mcp->mc_eax = 0; mcp->mc_edx = 0; } else { mcp->mc_eax = tp->tf_eax; mcp->mc_edx = tp->tf_edx; } mcp->mc_ecx = tp->tf_ecx; mcp->mc_eip = tp->tf_eip; mcp->mc_cs = tp->tf_cs; mcp->mc_eflags = tp->tf_eflags; mcp->mc_esp = tp->tf_esp; mcp->mc_ss = tp->tf_ss; mcp->mc_len = sizeof(*mcp); get_fpcontext(td, mcp); return (0); } /* * Set machine context. * * However, we don't set any but the user modifiable flags, and we won't * touch the cs selector. */ int set_mcontext(struct thread *td, const mcontext_t *mcp) { struct trapframe *tp; int eflags, ret; tp = td->td_frame; if (mcp->mc_len != sizeof(*mcp)) return (EINVAL); eflags = (mcp->mc_eflags & PSL_USERCHANGE) | (tp->tf_eflags & ~PSL_USERCHANGE); if ((ret = set_fpcontext(td, mcp)) == 0) { tp->tf_fs = mcp->mc_fs; tp->tf_es = mcp->mc_es; tp->tf_ds = mcp->mc_ds; tp->tf_edi = mcp->mc_edi; tp->tf_esi = mcp->mc_esi; tp->tf_ebp = mcp->mc_ebp; tp->tf_ebx = mcp->mc_ebx; tp->tf_edx = mcp->mc_edx; tp->tf_ecx = mcp->mc_ecx; tp->tf_eax = mcp->mc_eax; tp->tf_eip = mcp->mc_eip; tp->tf_eflags = eflags; tp->tf_esp = mcp->mc_esp; tp->tf_ss = mcp->mc_ss; td->td_pcb->pcb_gs = mcp->mc_gs; ret = 0; } return (ret); } static void get_fpcontext(struct thread *td, mcontext_t *mcp) { #ifndef DEV_NPX mcp->mc_fpformat = _MC_FPFMT_NODEV; mcp->mc_ownedfp = _MC_FPOWNED_NONE; #else union savefpu *addr; /* * XXX mc_fpstate might be misaligned, since its declaration is not * unportabilized using __attribute__((aligned(16))) like the * declaration of struct savemm, and anyway, alignment doesn't work * for auto variables since we don't use gcc's pessimal stack * alignment. Work around this by abusing the spare fields after * mcp->mc_fpstate. * * XXX unpessimize most cases by only aligning when fxsave might be * called, although this requires knowing too much about * npxgetregs()'s internals. */ addr = (union savefpu *)&mcp->mc_fpstate; if (td == PCPU_GET(fpcurthread) && #ifdef CPU_ENABLE_SSE cpu_fxsr && #endif ((uintptr_t)(void *)addr & 0xF)) { do addr = (void *)((char *)addr + 4); while ((uintptr_t)(void *)addr & 0xF); } mcp->mc_ownedfp = npxgetregs(td, addr); if (addr != (union savefpu *)&mcp->mc_fpstate) { bcopy(addr, &mcp->mc_fpstate, sizeof(mcp->mc_fpstate)); bzero(&mcp->mc_spare2, sizeof(mcp->mc_spare2)); } mcp->mc_fpformat = npxformat(); #endif } static int set_fpcontext(struct thread *td, const mcontext_t *mcp) { union savefpu *addr; if (mcp->mc_fpformat == _MC_FPFMT_NODEV) return (0); else if (mcp->mc_fpformat != _MC_FPFMT_387 && mcp->mc_fpformat != _MC_FPFMT_XMM) return (EINVAL); else if (mcp->mc_ownedfp == _MC_FPOWNED_NONE) /* We don't care what state is left in the FPU or PCB. */ fpstate_drop(td); else if (mcp->mc_ownedfp == _MC_FPOWNED_FPU || mcp->mc_ownedfp == _MC_FPOWNED_PCB) { /* XXX align as above. */ addr = (union savefpu *)&mcp->mc_fpstate; if (td == PCPU_GET(fpcurthread) && #ifdef CPU_ENABLE_SSE cpu_fxsr && #endif ((uintptr_t)(void *)addr & 0xF)) { do addr = (void *)((char *)addr + 4); while ((uintptr_t)(void *)addr & 0xF); bcopy(&mcp->mc_fpstate, addr, sizeof(mcp->mc_fpstate)); } #ifdef DEV_NPX /* * XXX we violate the dubious requirement that npxsetregs() * be called with interrupts disabled. */ npxsetregs(td, addr); #endif /* * Don't bother putting things back where they were in the * misaligned case, since we know that the caller won't use * them again. */ } else return (EINVAL); return (0); } static void fpstate_drop(struct thread *td) { register_t s; s = intr_disable(); #ifdef DEV_NPX if (PCPU_GET(fpcurthread) == td) npxdrop(); #endif /* * XXX force a full drop of the npx. The above only drops it if we * owned it. npxgetregs() has the same bug in the !cpu_fxsr case. * * XXX I don't much like npxgetregs()'s semantics of doing a full * drop. Dropping only to the pcb matches fnsave's behaviour. * We only need to drop to !PCB_INITDONE in sendsig(). But * sendsig() is the only caller of npxgetregs()... perhaps we just * have too many layers. */ curthread->td_pcb->pcb_flags &= ~PCB_NPXINITDONE; intr_restore(s); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { struct pcb *pcb; if (td == NULL) { dbregs->dr[0] = rdr0(); dbregs->dr[1] = rdr1(); dbregs->dr[2] = rdr2(); dbregs->dr[3] = rdr3(); dbregs->dr[4] = rdr4(); dbregs->dr[5] = rdr5(); dbregs->dr[6] = rdr6(); dbregs->dr[7] = rdr7(); } else { pcb = td->td_pcb; dbregs->dr[0] = pcb->pcb_dr0; dbregs->dr[1] = pcb->pcb_dr1; dbregs->dr[2] = pcb->pcb_dr2; dbregs->dr[3] = pcb->pcb_dr3; dbregs->dr[4] = 0; dbregs->dr[5] = 0; dbregs->dr[6] = pcb->pcb_dr6; dbregs->dr[7] = pcb->pcb_dr7; } return (0); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { struct pcb *pcb; int i; u_int32_t mask1, mask2; if (td == NULL) { load_dr0(dbregs->dr[0]); load_dr1(dbregs->dr[1]); load_dr2(dbregs->dr[2]); load_dr3(dbregs->dr[3]); load_dr4(dbregs->dr[4]); load_dr5(dbregs->dr[5]); load_dr6(dbregs->dr[6]); load_dr7(dbregs->dr[7]); } else { /* * Don't let an illegal value for dr7 get set. Specifically, * check for undefined settings. Setting these bit patterns * result in undefined behaviour and can lead to an unexpected * TRCTRAP. */ for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; i++, mask1 <<= 2, mask2 <<= 2) if ((dbregs->dr[7] & mask1) == mask2) return (EINVAL); pcb = td->td_pcb; /* * Don't let a process set a breakpoint that is not within the * process's address space. If a process could do this, it * could halt the system by setting a breakpoint in the kernel * (if ddb was enabled). Thus, we need to check to make sure * that no breakpoints are being enabled for addresses outside * process's address space, unless, perhaps, we were called by * uid 0. * * XXX - what about when the watched area of the user's * address space is written into from within the kernel * ... wouldn't that still cause a breakpoint to be generated * from within kernel mode? */ if (suser(td) != 0) { if (dbregs->dr[7] & 0x3) { /* dr0 is enabled */ if (dbregs->dr[0] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<2)) { /* dr1 is enabled */ if (dbregs->dr[1] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<4)) { /* dr2 is enabled */ if (dbregs->dr[2] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<6)) { /* dr3 is enabled */ if (dbregs->dr[3] >= VM_MAXUSER_ADDRESS) return (EINVAL); } } pcb->pcb_dr0 = dbregs->dr[0]; pcb->pcb_dr1 = dbregs->dr[1]; pcb->pcb_dr2 = dbregs->dr[2]; pcb->pcb_dr3 = dbregs->dr[3]; pcb->pcb_dr6 = dbregs->dr[6]; pcb->pcb_dr7 = dbregs->dr[7]; pcb->pcb_flags |= PCB_DBREGS; } return (0); } /* * Return > 0 if a hardware breakpoint has been hit, and the * breakpoint was in user space. Return 0, otherwise. */ int user_dbreg_trap(void) { u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ u_int32_t bp; /* breakpoint bits extracted from dr6 */ int nbp; /* number of breakpoints that triggered */ caddr_t addr[4]; /* breakpoint addresses */ int i; dr7 = rdr7(); if ((dr7 & 0x000000ff) == 0) { /* * all GE and LE bits in the dr7 register are zero, * thus the trap couldn't have been caused by the * hardware debug registers */ return 0; } nbp = 0; dr6 = rdr6(); bp = dr6 & 0x0000000f; if (!bp) { /* * None of the breakpoint bits are set meaning this * trap was not caused by any of the debug registers */ return 0; } /* * at least one of the breakpoints were hit, check to see * which ones and if any of them are user space addresses */ if (bp & 0x01) { addr[nbp++] = (caddr_t)rdr0(); } if (bp & 0x02) { addr[nbp++] = (caddr_t)rdr1(); } if (bp & 0x04) { addr[nbp++] = (caddr_t)rdr2(); } if (bp & 0x08) { addr[nbp++] = (caddr_t)rdr3(); } for (i=0; i, and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/pc98/pc98/machdep.c =================================================================== --- head/sys/pc98/pc98/machdep.c (revision 130343) +++ head/sys/pc98/pc98/machdep.c (revision 130344) @@ -1,2855 +1,2855 @@ /*- * 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. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_compat.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_isa.h" #include "opt_kstack_pages.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_npx.h" #include "opt_perfmon.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #include #endif #include #include #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #include #ifdef PERFMON #include #endif #ifdef SMP #include #include #endif #ifdef DEV_ISA #include #endif #ifdef PC98 #include #include #else #include #endif #include #include #include /* Sanity check for __curthread() */ CTASSERT(offsetof(struct pcpu, pc_curthread) == 0); extern void init386(int first); extern void dblfault_handler(void); extern void printcpuinfo(void); /* XXX header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) #if !defined(CPU_ENABLE_SSE) && defined(I686_CPU) #define CPU_ENABLE_SSE #endif #if defined(CPU_DISABLE_SSE) #undef CPU_ENABLE_SSE #endif static void cpu_startup(void *); static void fpstate_drop(struct thread *td); static void get_fpcontext(struct thread *td, mcontext_t *mcp); static int set_fpcontext(struct thread *td, const mcontext_t *mcp); #ifdef CPU_ENABLE_SSE static void set_fpregs_xmm(struct save87 *, struct savexmm *); static void fill_fpregs_xmm(struct savexmm *, struct save87 *); #endif /* CPU_ENABLE_SSE */ SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) #ifdef PC98 int need_pre_dma_flush; /* If 1, use wbinvd befor DMA transfer. */ int need_post_dma_flush; /* If 1, use invd after DMA transfer. */ #endif int _udatasel, _ucodesel; u_int basemem; #ifdef PC98 static int ispc98 = 1; #else static int ispc98 = 0; #endif SYSCTL_INT(_machdep, OID_AUTO, ispc98, CTLFLAG_RD, &ispc98, 0, ""); int cold = 1; #ifdef COMPAT_43 static void osendsig(sig_t catcher, int sig, sigset_t *mask, u_long code); #endif #ifdef COMPAT_FREEBSD4 static void freebsd4_sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code); #endif long Maxmem = 0; #ifdef PC98 int Maxmem_under16M = 0; #endif vm_paddr_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) struct kva_md_info kmi; static struct trapframe proc0_tf; #ifndef SMP static struct pcpu __pcpu; #endif struct mtx icu_lock; static void cpu_startup(dummy) void *dummy; { /* * Good {morning,afternoon,evening,night}. */ startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %ju (%ju MB)\n", ptoa((uintmax_t)Maxmem), ptoa((uintmax_t)Maxmem) / 1048576); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { vm_paddr_t size; size = phys_avail[indx + 1] - phys_avail[indx]; printf( "0x%016jx - 0x%016jx, %ju bytes (%ju pages)\n", (uintmax_t)phys_avail[indx], (uintmax_t)phys_avail[indx + 1] - 1, (uintmax_t)size, (uintmax_t)size / PAGE_SIZE); } } vm_ksubmap_init(&kmi); printf("avail memory = %ju (%ju MB)\n", ptoa((uintmax_t)cnt.v_free_count), ptoa((uintmax_t)cnt.v_free_count) / 1048576); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); cpu_setregs(); } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top 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. */ #ifdef COMPAT_43 static void osendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct osigframe sf, *fp; struct proc *p; struct thread *td; struct sigacts *psp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct osigframe *)(td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct osigframe)); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else fp = (struct osigframe *)regs->tf_esp - 1; /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_arg2 = (register_t)&fp->sf_siginfo; sf.sf_siginfo.si_signo = sig; sf.sf_siginfo.si_code = code; sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; } else { /* Old FreeBSD-style arguments. */ sf.sf_arg2 = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* Save most if not all of trap frame. */ sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; sf.sf_siginfo.si_sc.sc_es = regs->tf_es; sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; sf.sf_siginfo.si_sc.sc_gs = rgs(); sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; /* Build the signal context to be used by osigreturn(). */ sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; sf.sf_siginfo.si_sc.sc_err = regs->tf_err; /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_siginfo.si_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* See sendsig() for comments. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(*fp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - szosigcode; regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; load_gs(_udatasel); regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } #endif /* COMPAT_43 */ #ifdef COMPAT_FREEBSD4 static void freebsd4_sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe4 sf, *sfp; struct proc *p; struct thread *td; struct sigacts *psp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe4 *)(td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct sigframe4)); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe4 *)regs->tf_esp - 1; /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - szfreebsd4_sigcode; regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } #endif /* COMPAT_FREEBSD4 */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe sf, *sfp; struct proc *p; struct thread *td; struct sigacts *psp; char *sp; struct trapframe *regs; int oonstack; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); #ifdef COMPAT_FREEBSD4 if (SIGISMEMBER(psp->ps_freebsd4, sig)) { freebsd4_sendsig(catcher, sig, mask, code); return; } #endif #ifdef COMPAT_43 if (SIGISMEMBER(psp->ps_osigset, sig)) { osendsig(catcher, sig, mask, code); return; } #endif regs = td->td_frame; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext); /* magic */ get_fpcontext(td, &sf.sf_uc.uc_mcontext); fpstate_drop(td); /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sp = td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct sigframe); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sp = (char *)regs->tf_esp - sizeof(struct sigframe); /* Align to 16 bytes. */ sfp = (struct sigframe *)((unsigned int)sp & ~0xF); /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill in POSIX parts */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); } /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { #ifdef DEBUG printf("process %ld has trashed its stack\n", (long)p->p_pid); #endif PROC_LOCK(p); sigexit(td, SIGILL); } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_eflags &= ~PSL_T; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } /* * Build siginfo_t for SA thread */ void cpu_thread_siginfo(int sig, u_long code, siginfo_t *si) { struct proc *p; struct thread *td; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); bzero(si, sizeof(*si)); si->si_signo = sig; si->si_code = code; si->si_addr = (void *)td->td_frame->tf_err; /* XXXKSE fill other fields */ } /* * 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 * state to gain improper privileges. * * MPSAFE */ #ifdef COMPAT_43 int osigreturn(td, uap) struct thread *td; struct osigreturn_args /* { struct osigcontext *sigcntxp; } */ *uap; { struct osigcontext sc; struct trapframe *regs; struct osigcontext *scp; struct proc *p = td->td_proc; int eflags, error; regs = td->td_frame; error = copyin(uap->sigcntxp, &sc, sizeof(sc)); if (error != 0) return (error); scp = ≻ eflags = scp->sc_ps; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ if (!CS_SECURE(scp->sc_cs)) { trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; regs->tf_fs = scp->sc_fs; } /* Restore remaining registers. */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (scp->sc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif SIGSETOLD(td->td_sigmask, scp->sc_mask); SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } #endif /* COMPAT_43 */ #ifdef COMPAT_FREEBSD4 /* * MPSAFE */ int freebsd4_sigreturn(td, uap) struct thread *td; struct freebsd4_sigreturn_args /* { const ucontext4 *sigcntxp; } */ *uap; { struct ucontext4 uc; struct proc *p = td->td_proc; struct trapframe *regs; const struct ucontext4 *ucp; int cs, eflags, error; error = copyin(uap->sigcntxp, &uc, sizeof(uc)); if (error != 0) return (error); ucp = &uc; regs = td->td_frame; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("freebsd4_sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("freebsd4_sigreturn: cs = 0x%x\n", cs); trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (ucp->uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = ucp->uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } #endif /* COMPAT_FREEBSD4 */ /* * MPSAFE */ int sigreturn(td, uap) struct thread *td; struct sigreturn_args /* { const __ucontext *sigcntxp; } */ *uap; { ucontext_t uc; struct proc *p = td->td_proc; struct trapframe *regs; const ucontext_t *ucp; int cs, eflags, error, ret; error = copyin(uap->sigcntxp, &uc, sizeof(uc)); if (error != 0) return (error); ucp = &uc; regs = td->td_frame; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (td->td_pcb->pcb_ext == 0) return (EINVAL); vm86 = &td->td_pcb->pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(td, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("sigreturn: cs = 0x%x\n", cs); trapsignal(td, SIGBUS, T_PROTFLT); return (EINVAL); } ret = set_fpcontext(td, &ucp->uc_mcontext); if (ret != 0) return (ret); bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) if (ucp->uc_mcontext.mc_onstack & 1) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif td->td_sigmask = ucp->uc_sigmask; SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); return (EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Hook to idle the CPU when possible. In the SMP case we default to * off because a halted cpu will not currently pick up a new thread in the * run queue until the next timer tick. If turned on this will result in * approximately a 4.2% loss in real time performance in buildworld tests * (but improves user and sys times oddly enough), and saves approximately * 5% in power consumption on an idle machine (tests w/2xCPU 1.1GHz P3). * * XXX we need to have a cpu mask of idle cpus and generate an IPI or * otherwise generate some sort of interrupt to wake up cpus sitting in HLT. * Then we can have our cake and eat it too. * * XXX I'm turning it on for SMP as well by default for now. It seems to * help lock contention somewhat, and this is critical for HTT. -Peter */ static int cpu_idle_hlt = 1; SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW, &cpu_idle_hlt, 0, "Idle loop HLT enable"); static void cpu_idle_default(void) { /* * we must absolutely guarentee that hlt is the * absolute next instruction after sti or we * introduce a timing window. */ __asm __volatile("sti; hlt"); } /* * Note that we have to be careful here to avoid a race between checking * sched_runnable() and actually halting. If we don't do this, we may waste * the time between calling hlt and the next interrupt even though there * is a runnable process. */ void cpu_idle(void) { #ifdef SMP if (mp_grab_cpu_hlt()) return; #endif if (cpu_idle_hlt) { disable_intr(); if (sched_runnable()) enable_intr(); else (*cpu_idle_hook)(); } } /* Other subsystems (e.g., ACPI) can hook this later. */ void (*cpu_idle_hook)(void) = cpu_idle_default; /* * Clear registers on exec */ void exec_setregs(td, entry, stack, ps_strings) struct thread *td; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = td->td_frame; struct pcb *pcb = td->td_pcb; /* Reset pc->pcb_gs and %gs before possibly invalidating it. */ pcb->pcb_gs = _udatasel; load_gs(_udatasel); if (td->td_proc->p_md.md_ldt) user_ldt_free(td); bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* * Reset the hardware debug registers if they were in use. * They won't have any meaning for the newly exec'd process. */ if (pcb->pcb_flags & PCB_DBREGS) { pcb->pcb_dr0 = 0; pcb->pcb_dr1 = 0; pcb->pcb_dr2 = 0; pcb->pcb_dr3 = 0; pcb->pcb_dr6 = 0; pcb->pcb_dr7 = 0; if (pcb == PCPU_GET(curpcb)) { /* * Clear the debug registers on the running * CPU, otherwise they will end up affecting * the next process we switch to. */ reset_dbregs(); } pcb->pcb_flags &= ~PCB_DBREGS; } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ td->td_pcb->pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); /* Initialize the npx (if any) for the current process. */ /* * XXX the above load_cr0() also initializes it and is a layering * violation if NPX is configured. It drops the npx partially * and this would be fatal if we were interrupted now, and decided * to force the state to the pcb, and checked the invariant * (CR0_TS clear) if and only if PCPU_GET(fpcurthread) != NULL). * ALL of this can happen except the check. The check used to * happen and be fatal later when we didn't complete the drop * before returning to user mode. This should be fixed properly * soon. */ fpstate_drop(td); /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ td->td_retval[1] = 0; } void cpu_setregs(void) { unsigned int cr0; cr0 = rcr0(); #ifdef SMP cr0 |= CR0_NE; /* Done by npxinit() */ #endif cr0 |= CR0_MP | CR0_TS; /* Done at every execve() too. */ #ifndef I386_CPU cr0 |= CR0_WP | CR0_AM; #endif load_cr0(cr0); load_gs(_udatasel); } static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); u_long bootdev; /* not a dev_t - encoding is different */ SYSCTL_ULONG(_machdep, OID_AUTO, guessed_bootdev, CTLFLAG_RD, &bootdev, 0, "Maybe the Boot device (not in dev_t format)"); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ struct region_descriptor r_gdt, r_idt; /* table descriptors */ int private_tss; /* flag indicating private tss */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0uarea; extern vm_offset_t proc0kstack; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPRIV_SEL 3 SMP Per-Processor Private Data Descriptor */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 4 Proc 0 Tss Descriptor */ { 0x0, /* segment base address */ sizeof(struct i386tss)-1,/* length */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 5 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 6 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 7 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSLOWMEM_SEL 8 BIOS access to realmode segment 0x40, must be #8 in GDT */ { 0x400, /* segment base address */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 9 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE32_SEL 10 BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE16_SEL 11 BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSDATA_SEL 12 BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSUTIL_SEL 13 BIOS 16-bit interface (Utility) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSARGS_SEL 14 BIOS 16-bit interface (Arguments) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(xmm), IDTVEC(lcall_syscall), IDTVEC(int0x80_syscall); #ifdef DDB /* * Display the index and function name of any IDT entries that don't use * the default 'rsvd' entry point. */ DB_SHOW_COMMAND(idt, db_show_idt) { struct gate_descriptor *ip; int idx, quit; uintptr_t func; ip = idt; db_setup_paging(db_simple_pager, &quit, DB_LINES_PER_PAGE); for (idx = 0, quit = 0; idx < NIDT; idx++) { func = (ip->gd_hioffset << 16 | ip->gd_looffset); if (func != (uintptr_t)&IDTVEC(rsvd)) { db_printf("%3d\t", idx); db_printsym(func, DB_STGY_PROC); db_printf("\n"); } ip++; } } #endif void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } #define PHYSMAP_SIZE (2 * 8) /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. * * XXX first should be vm_paddr_t. */ static void getmemsize(int first) { #ifdef PC98 int i, physmap_idx, pa_indx, pg_n; u_int extmem, under16; vm_offset_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t *pte; char *cp; #else int i, physmap_idx, pa_indx; u_int extmem; struct vm86frame vmf; struct vm86context vmc; vm_paddr_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t *pte; char *cp; struct bios_smap *smap; #endif #ifdef PC98 /* XXX - some of EPSON machines can't use PG_N */ pg_n = PG_N; if (pc98_machine_type & M_EPSON_PC98) { switch (epson_machine_id) { #ifdef WB_CACHE default: #endif case 0x34: /* PC-486HX */ case 0x35: /* PC-486HG */ case 0x3B: /* PC-486HA */ pg_n = 0; break; } } bzero(physmap, sizeof(physmap)); /* * Perform "base memory" related probes & setup */ under16 = pc98_getmemsize(&basemem, &extmem); if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) pmap_kenter(KERNBASE + pa, pa); /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ pte = (pt_entry_t *)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; #else /* PC98 */ bzero(&vmf, sizeof(struct vm86frame)); bzero(physmap, sizeof(physmap)); basemem = 0; /* * map page 1 R/W into the kernel page table so we can use it * as a buffer. The kernel will unmap this page later. */ pmap_kenter(KERNBASE + (1 << PAGE_SHIFT), 1 << PAGE_SHIFT); /* * get memory map with INT 15:E820 */ vmc.npages = 0; smap = (void *)vm86_addpage(&vmc, 1, KERNBASE + (1 << PAGE_SHIFT)); vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di); physmap_idx = 0; vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = sizeof(struct bios_smap); i = vm86_datacall(0x15, &vmf, &vmc); if (i || vmf.vmf_eax != SMAP_SIG) break; if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%016llx len=%016llx\n", smap->type, smap->base, smap->length); if (smap->type != 0x01) goto next_run; if (smap->length == 0) goto next_run; if (smap->base >= 0xffffffff) { printf("%uK of memory above 4GB ignored\n", (u_int)(smap->length / 1024)); goto next_run; } for (i = 0; i <= physmap_idx; i += 2) { if (smap->base < physmap[i + 1]) { if (boothowto & RB_VERBOSE) printf( "Overlapping or non-montonic memory region, ignoring second region\n"); goto next_run; } } if (smap->base == physmap[physmap_idx + 1]) { physmap[physmap_idx + 1] += smap->length; goto next_run; } physmap_idx += 2; if (physmap_idx == PHYSMAP_SIZE) { printf( "Too many segments in the physical address map, giving up\n"); break; } physmap[physmap_idx] = smap->base; physmap[physmap_idx + 1] = smap->base + smap->length; next_run: ; } while (vmf.vmf_ebx != 0); /* * Perform "base memory" related probes & setup */ for (i = 0; i <= physmap_idx; i += 2) { if (physmap[i] == 0x00000000) { basemem = physmap[i + 1] / 1024; break; } } /* Fall back to the old compatibility function for base memory */ if (basemem == 0) { vm86_intcall(0x12, &vmf); basemem = vmf.vmf_ax; } if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) pmap_kenter(KERNBASE + pa, pa); /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ pte = (pt_entry_t *)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; if (physmap[1] != 0) goto physmap_done; /* * If we failed above, try memory map with INT 15:E801 */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { extmem = vmf.vmf_cx + vmf.vmf_dx * 64; } else { #if 0 vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); extmem = vmf.vmf_ax; #else /* * Prefer the RTC value for extended memory. */ extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8); #endif } /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. * * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((extmem > 15 * 1024) && (extmem < 16 * 1024)) extmem = 15 * 1024; #endif /* PC98 */ physmap[0] = 0; physmap[1] = basemem * 1024; physmap_idx = 2; physmap[physmap_idx] = 0x100000; physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; #ifdef PC98 if ((under16 != 16 * 1024) && (extmem > 15 * 1024)) { /* 15M - 16M region is cut off, so need to divide chunk */ physmap[physmap_idx + 1] = under16 * 1024; physmap_idx += 2; physmap[physmap_idx] = 0x1000000; physmap[physmap_idx + 1] = physmap[2] + extmem * 1024; } #else physmap_done: #endif /* * Now, physmap contains a map of physical memory. */ #ifdef SMP /* make hole for AP bootstrap code */ physmap[1] = mp_bootaddress(physmap[1]); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif /* * hw.physmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ if ((cp = getenv("hw.physmem")) != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(cp, &ep, 0); if ((ep != cp) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", cp); else Maxmem = atop(AllowMem); freeenv(cp); } if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %ldK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa((vm_paddr_t)Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(first, 0); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; pte = CMAP1; /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_paddr_t end; end = ptoa((vm_paddr_t)Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad; int *ptr = (int *)CADDR1; /* * block out kernel memory as not available. */ if (pa >= KERNLOAD && pa < first) continue; page_bad = FALSE; /* * map page into kernel: valid, read/write,non-cacheable */ #ifdef PC98 *pte = pa | PG_V | PG_RW | pg_n; #else *pte = pa | PG_V | PG_RW | PG_N; #endif invltlb(); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) { page_bad = TRUE; } /* * Restore original value. */ *(int *)ptr = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) { continue; } /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf( "Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; } } *pte = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; } void init386(first) int first; { struct gate_descriptor *gdp; int gsel_tss, metadata_missing, off, x; struct pcpu *pc; proc0.p_uarea = proc0uarea; thread0.td_kstack = proc0kstack; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; /* * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ proc_linkup(&proc0, &ksegrp0, &kse0, &thread0); #ifdef PC98 /* * Initialize DMAC */ pc98_init_dmac(); #endif metadata_missing = 0; if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } else { metadata_missing = 1; } if (envmode == 1) kern_envp = static_env; else if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* Init basic tunables, hz etc */ init_param1(); /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = atop(0 - 1); gdt_segs[GDATA_SEL].ssd_limit = atop(0 - 1); #ifdef SMP pc = &SMP_prvspace[0].pcpu; gdt_segs[GPRIV_SEL].ssd_limit = atop(sizeof(struct privatespace) - 1); #else pc = &__pcpu; gdt_segs[GPRIV_SEL].ssd_limit = atop(sizeof(struct pcpu) - 1); #endif gdt_segs[GPRIV_SEL].ssd_base = (int) pc; gdt_segs[GPROC0_SEL].ssd_base = (int) &pc->pc_common_tss; for (x = 0; x < NGDT; x++) ssdtosd(&gdt_segs[x], &gdt[x].sd); r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); pcpu_init(pc, 0, sizeof(struct pcpu)); PCPU_SET(prvspace, pc); PCPU_SET(curthread, &thread0); PCPU_SET(curpcb, thread0.td_pcb); /* * Initialize mutexes. * * icu_lock: in order to allow an interrupt to occur in a critical * section, to set pcpu->ipending (etc...) properly, we * must be able to get the icu lock, so it can't be * under witness. */ mutex_init(); mtx_init(&clock_lock, "clk", NULL, MTX_SPIN); mtx_init(&icu_lock, "icu", NULL, MTX_SPIN | MTX_NOWITNESS); /* make ldt memory segments */ /* * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ ldt_segs[LUCODE_SEL].ssd_limit = atop(VM_MAXUSER_ADDRESS - 1); ldt_segs[LUDATA_SEL].ssd_limit = atop(VM_MAXUSER_ADDRESS - 1); for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DE, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DB, &IDTVEC(dbg), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NMI, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_BP, &IDTVEC(bpt), SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_OF, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_BR, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_UD, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NM, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL , GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DF, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(IDT_FPUGP, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_TS, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NP, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_SS, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_GP, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_PF, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_MF, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_AC, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_MC, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_XF, &IDTVEC(xmm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); /* * Initialize the console before we print anything out. */ cninit(); if (metadata_missing) printf("WARNING: loader(8) metadata is missing!\n"); #ifdef DEV_ISA atpic_startup(); #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(IDT_UD, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_GP, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ /* Note: -16 is so we can grow the trapframe if we came from vm86 */ PCPU_SET(common_tss.tss_esp0, thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE - sizeof(struct pcb) - 16); PCPU_SET(common_tss.tss_ss0, GSEL(GDATA_SEL, SEL_KPL)); gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); private_tss = 0; PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); PCPU_SET(common_tss.tss_ioopt, (sizeof (struct i386tss)) << 16); ltr(gsel_tss); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int)&dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = (int)IdlePTD; dblfault_tss.tss_eip = (int)dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_fs = GSEL(GPRIV_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); vm86_initialize(); getmemsize(first); init_param2(physmem); /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(lcall_syscall); gdp->gd_looffset = x; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = x >> 16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ thread0.td_pcb->pcb_flags = 0; /* XXXKSE */ thread0.td_pcb->pcb_cr3 = (int)IdlePTD; thread0.td_pcb->pcb_ext = 0; thread0.td_frame = &proc0_tf; } void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) { } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL) static void f00f_hack(void *unused) { struct gate_descriptor *new_idt; vm_offset_t tmp; if (!has_f00f_bug) return; GIANT_REQUIRED; printf("Intel Pentium detected, installing workaround for F00F bug\n"); tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); /* Put the problematic entry (#6) at the end of the lower page. */ new_idt = (struct gate_descriptor*) (tmp + PAGE_SIZE - 7 * sizeof(struct gate_descriptor)); bcopy(idt, new_idt, sizeof(idt0)); r_idt.rd_base = (u_int)new_idt; lidt(&r_idt); idt = new_idt; if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(struct thread *td, u_long addr) { td->td_frame->tf_eip = addr; return (0); } int ptrace_single_step(struct thread *td) { td->td_frame->tf_eflags |= PSL_T; return (0); } int fill_regs(struct thread *td, struct reg *regs) { struct pcb *pcb; struct trapframe *tp; tp = td->td_frame; regs->r_fs = tp->tf_fs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = td->td_pcb; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(struct thread *td, struct reg *regs) { struct pcb *pcb; struct trapframe *tp; tp = td->td_frame; if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_fs = regs->r_fs; tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = td->td_pcb; pcb->pcb_gs = regs->r_gs; return (0); } #ifdef CPU_ENABLE_SSE static void fill_fpregs_xmm(sv_xmm, sv_87) struct savexmm *sv_xmm; struct save87 *sv_87; { register struct env87 *penv_87 = &sv_87->sv_env; register struct envxmm *penv_xmm = &sv_xmm->sv_env; int i; bzero(sv_87, sizeof(*sv_87)); /* FPU control/status */ penv_87->en_cw = penv_xmm->en_cw; penv_87->en_sw = penv_xmm->en_sw; penv_87->en_tw = penv_xmm->en_tw; penv_87->en_fip = penv_xmm->en_fip; penv_87->en_fcs = penv_xmm->en_fcs; penv_87->en_opcode = penv_xmm->en_opcode; penv_87->en_foo = penv_xmm->en_foo; penv_87->en_fos = penv_xmm->en_fos; /* FPU registers */ for (i = 0; i < 8; ++i) sv_87->sv_ac[i] = sv_xmm->sv_fp[i].fp_acc; } static void set_fpregs_xmm(sv_87, sv_xmm) struct save87 *sv_87; struct savexmm *sv_xmm; { register struct env87 *penv_87 = &sv_87->sv_env; register struct envxmm *penv_xmm = &sv_xmm->sv_env; int i; /* FPU control/status */ penv_xmm->en_cw = penv_87->en_cw; penv_xmm->en_sw = penv_87->en_sw; penv_xmm->en_tw = penv_87->en_tw; penv_xmm->en_fip = penv_87->en_fip; penv_xmm->en_fcs = penv_87->en_fcs; penv_xmm->en_opcode = penv_87->en_opcode; penv_xmm->en_foo = penv_87->en_foo; penv_xmm->en_fos = penv_87->en_fos; /* FPU registers */ for (i = 0; i < 8; ++i) sv_xmm->sv_fp[i].fp_acc = sv_87->sv_ac[i]; } #endif /* CPU_ENABLE_SSE */ int fill_fpregs(struct thread *td, struct fpreg *fpregs) { #ifdef CPU_ENABLE_SSE if (cpu_fxsr) { fill_fpregs_xmm(&td->td_pcb->pcb_save.sv_xmm, (struct save87 *)fpregs); return (0); } #endif /* CPU_ENABLE_SSE */ bcopy(&td->td_pcb->pcb_save.sv_87, fpregs, sizeof *fpregs); return (0); } int set_fpregs(struct thread *td, struct fpreg *fpregs) { #ifdef CPU_ENABLE_SSE if (cpu_fxsr) { set_fpregs_xmm((struct save87 *)fpregs, &td->td_pcb->pcb_save.sv_xmm); return (0); } #endif /* CPU_ENABLE_SSE */ bcopy(fpregs, &td->td_pcb->pcb_save.sv_87, sizeof *fpregs); return (0); } /* * Get machine context. */ int get_mcontext(struct thread *td, mcontext_t *mcp, int flags) { struct trapframe *tp; tp = td->td_frame; PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(tp->tf_esp); PROC_UNLOCK(curthread->td_proc); mcp->mc_gs = td->td_pcb->pcb_gs; mcp->mc_fs = tp->tf_fs; mcp->mc_es = tp->tf_es; mcp->mc_ds = tp->tf_ds; mcp->mc_edi = tp->tf_edi; mcp->mc_esi = tp->tf_esi; mcp->mc_ebp = tp->tf_ebp; mcp->mc_isp = tp->tf_isp; mcp->mc_ebx = tp->tf_ebx; if (flags & GET_MC_CLEAR_RET) { mcp->mc_eax = 0; mcp->mc_edx = 0; } else { mcp->mc_eax = tp->tf_eax; mcp->mc_edx = tp->tf_edx; } mcp->mc_ecx = tp->tf_ecx; mcp->mc_eip = tp->tf_eip; mcp->mc_cs = tp->tf_cs; mcp->mc_eflags = tp->tf_eflags; mcp->mc_esp = tp->tf_esp; mcp->mc_ss = tp->tf_ss; mcp->mc_len = sizeof(*mcp); get_fpcontext(td, mcp); return (0); } /* * Set machine context. * * However, we don't set any but the user modifiable flags, and we won't * touch the cs selector. */ int set_mcontext(struct thread *td, const mcontext_t *mcp) { struct trapframe *tp; int eflags, ret; tp = td->td_frame; if (mcp->mc_len != sizeof(*mcp)) return (EINVAL); eflags = (mcp->mc_eflags & PSL_USERCHANGE) | (tp->tf_eflags & ~PSL_USERCHANGE); if ((ret = set_fpcontext(td, mcp)) == 0) { tp->tf_fs = mcp->mc_fs; tp->tf_es = mcp->mc_es; tp->tf_ds = mcp->mc_ds; tp->tf_edi = mcp->mc_edi; tp->tf_esi = mcp->mc_esi; tp->tf_ebp = mcp->mc_ebp; tp->tf_ebx = mcp->mc_ebx; tp->tf_edx = mcp->mc_edx; tp->tf_ecx = mcp->mc_ecx; tp->tf_eax = mcp->mc_eax; tp->tf_eip = mcp->mc_eip; tp->tf_eflags = eflags; tp->tf_esp = mcp->mc_esp; tp->tf_ss = mcp->mc_ss; td->td_pcb->pcb_gs = mcp->mc_gs; ret = 0; } return (ret); } static void get_fpcontext(struct thread *td, mcontext_t *mcp) { #ifndef DEV_NPX mcp->mc_fpformat = _MC_FPFMT_NODEV; mcp->mc_ownedfp = _MC_FPOWNED_NONE; #else union savefpu *addr; /* * XXX mc_fpstate might be misaligned, since its declaration is not * unportabilized using __attribute__((aligned(16))) like the * declaration of struct savemm, and anyway, alignment doesn't work * for auto variables since we don't use gcc's pessimal stack * alignment. Work around this by abusing the spare fields after * mcp->mc_fpstate. * * XXX unpessimize most cases by only aligning when fxsave might be * called, although this requires knowing too much about * npxgetregs()'s internals. */ addr = (union savefpu *)&mcp->mc_fpstate; if (td == PCPU_GET(fpcurthread) && #ifdef CPU_ENABLE_SSE cpu_fxsr && #endif ((uintptr_t)(void *)addr & 0xF)) { do addr = (void *)((char *)addr + 4); while ((uintptr_t)(void *)addr & 0xF); } mcp->mc_ownedfp = npxgetregs(td, addr); if (addr != (union savefpu *)&mcp->mc_fpstate) { bcopy(addr, &mcp->mc_fpstate, sizeof(mcp->mc_fpstate)); bzero(&mcp->mc_spare2, sizeof(mcp->mc_spare2)); } mcp->mc_fpformat = npxformat(); #endif } static int set_fpcontext(struct thread *td, const mcontext_t *mcp) { union savefpu *addr; if (mcp->mc_fpformat == _MC_FPFMT_NODEV) return (0); else if (mcp->mc_fpformat != _MC_FPFMT_387 && mcp->mc_fpformat != _MC_FPFMT_XMM) return (EINVAL); else if (mcp->mc_ownedfp == _MC_FPOWNED_NONE) /* We don't care what state is left in the FPU or PCB. */ fpstate_drop(td); else if (mcp->mc_ownedfp == _MC_FPOWNED_FPU || mcp->mc_ownedfp == _MC_FPOWNED_PCB) { /* XXX align as above. */ addr = (union savefpu *)&mcp->mc_fpstate; if (td == PCPU_GET(fpcurthread) && #ifdef CPU_ENABLE_SSE cpu_fxsr && #endif ((uintptr_t)(void *)addr & 0xF)) { do addr = (void *)((char *)addr + 4); while ((uintptr_t)(void *)addr & 0xF); bcopy(&mcp->mc_fpstate, addr, sizeof(mcp->mc_fpstate)); } #ifdef DEV_NPX /* * XXX we violate the dubious requirement that npxsetregs() * be called with interrupts disabled. */ npxsetregs(td, addr); #endif /* * Don't bother putting things back where they were in the * misaligned case, since we know that the caller won't use * them again. */ } else return (EINVAL); return (0); } static void fpstate_drop(struct thread *td) { register_t s; s = intr_disable(); #ifdef DEV_NPX if (PCPU_GET(fpcurthread) == td) npxdrop(); #endif /* * XXX force a full drop of the npx. The above only drops it if we * owned it. npxgetregs() has the same bug in the !cpu_fxsr case. * * XXX I don't much like npxgetregs()'s semantics of doing a full * drop. Dropping only to the pcb matches fnsave's behaviour. * We only need to drop to !PCB_INITDONE in sendsig(). But * sendsig() is the only caller of npxgetregs()... perhaps we just * have too many layers. */ curthread->td_pcb->pcb_flags &= ~PCB_NPXINITDONE; intr_restore(s); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { struct pcb *pcb; if (td == NULL) { dbregs->dr[0] = rdr0(); dbregs->dr[1] = rdr1(); dbregs->dr[2] = rdr2(); dbregs->dr[3] = rdr3(); dbregs->dr[4] = rdr4(); dbregs->dr[5] = rdr5(); dbregs->dr[6] = rdr6(); dbregs->dr[7] = rdr7(); } else { pcb = td->td_pcb; dbregs->dr[0] = pcb->pcb_dr0; dbregs->dr[1] = pcb->pcb_dr1; dbregs->dr[2] = pcb->pcb_dr2; dbregs->dr[3] = pcb->pcb_dr3; dbregs->dr[4] = 0; dbregs->dr[5] = 0; dbregs->dr[6] = pcb->pcb_dr6; dbregs->dr[7] = pcb->pcb_dr7; } return (0); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { struct pcb *pcb; int i; u_int32_t mask1, mask2; if (td == NULL) { load_dr0(dbregs->dr[0]); load_dr1(dbregs->dr[1]); load_dr2(dbregs->dr[2]); load_dr3(dbregs->dr[3]); load_dr4(dbregs->dr[4]); load_dr5(dbregs->dr[5]); load_dr6(dbregs->dr[6]); load_dr7(dbregs->dr[7]); } else { /* * Don't let an illegal value for dr7 get set. Specifically, * check for undefined settings. Setting these bit patterns * result in undefined behaviour and can lead to an unexpected * TRCTRAP. */ for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; i++, mask1 <<= 2, mask2 <<= 2) if ((dbregs->dr[7] & mask1) == mask2) return (EINVAL); pcb = td->td_pcb; /* * Don't let a process set a breakpoint that is not within the * process's address space. If a process could do this, it * could halt the system by setting a breakpoint in the kernel * (if ddb was enabled). Thus, we need to check to make sure * that no breakpoints are being enabled for addresses outside * process's address space, unless, perhaps, we were called by * uid 0. * * XXX - what about when the watched area of the user's * address space is written into from within the kernel * ... wouldn't that still cause a breakpoint to be generated * from within kernel mode? */ if (suser(td) != 0) { if (dbregs->dr[7] & 0x3) { /* dr0 is enabled */ if (dbregs->dr[0] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<2)) { /* dr1 is enabled */ if (dbregs->dr[1] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<4)) { /* dr2 is enabled */ if (dbregs->dr[2] >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr[7] & (0x3<<6)) { /* dr3 is enabled */ if (dbregs->dr[3] >= VM_MAXUSER_ADDRESS) return (EINVAL); } } pcb->pcb_dr0 = dbregs->dr[0]; pcb->pcb_dr1 = dbregs->dr[1]; pcb->pcb_dr2 = dbregs->dr[2]; pcb->pcb_dr3 = dbregs->dr[3]; pcb->pcb_dr6 = dbregs->dr[6]; pcb->pcb_dr7 = dbregs->dr[7]; pcb->pcb_flags |= PCB_DBREGS; } return (0); } /* * Return > 0 if a hardware breakpoint has been hit, and the * breakpoint was in user space. Return 0, otherwise. */ int user_dbreg_trap(void) { u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ u_int32_t bp; /* breakpoint bits extracted from dr6 */ int nbp; /* number of breakpoints that triggered */ caddr_t addr[4]; /* breakpoint addresses */ int i; dr7 = rdr7(); if ((dr7 & 0x000000ff) == 0) { /* * all GE and LE bits in the dr7 register are zero, * thus the trap couldn't have been caused by the * hardware debug registers */ return 0; } nbp = 0; dr6 = rdr6(); bp = dr6 & 0x0000000f; if (!bp) { /* * None of the breakpoint bits are set meaning this * trap was not caused by any of the debug registers */ return 0; } /* * at least one of the breakpoints were hit, check to see * which ones and if any of them are user space addresses */ if (bp & 0x01) { addr[nbp++] = (caddr_t)rdr0(); } if (bp & 0x02) { addr[nbp++] = (caddr_t)rdr1(); } if (bp & 0x04) { addr[nbp++] = (caddr_t)rdr2(); } if (bp & 0x08) { addr[nbp++] = (caddr_t)rdr3(); } for (i=0; i, and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/pc98/pc98/sio.c =================================================================== --- head/sys/pc98/pc98/sio.c (revision 130343) +++ head/sys/pc98/pc98/sio.c (revision 130344) @@ -1,5043 +1,5043 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * 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. * 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. * * $FreeBSD$ * from: @(#)com.c 7.5 (Berkeley) 5/16/91 * from: i386/isa sio.c,v 1.234 */ #include "opt_comconsole.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_sio.h" /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ /*=============================================================== * 386BSD(98),FreeBSD-1.1x(98) com driver. * ----- * modified for PC9801 by M.Ishii * Kyoto University Microcomputer Club (KMC) * Chou "TEFUTEFU" Hirotomi * Kyoto Univ. the faculty of medicine *=============================================================== * FreeBSD-2.0.1(98) sio driver. * ----- * modified for pc98 Internal i8251 and MICRO CORE MC16550II * T.Koike(hfc01340@niftyserve.or.jp) * implement kernel device configuration * aizu@orient.center.nitech.ac.jp * * Notes. * ----- * PC98 localization based on 386BSD(98) com driver. Using its PC98 local * functions. * This driver is under debugging,has bugs. */ /* * modified for AIWA B98-01 * by T.Hatanou last update: 15 Sep.1995 */ /* * Modified by Y.Takahashi of Kogakuin University. */ /* * modified for 8251(FIFO) by Seigo TANIMURA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if DDB > 0 #include #endif #include #include #include #include #ifdef PC98 #include #include #endif #ifdef COM_ESP #include #endif #include #ifdef PC98 #include #include #endif #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_TO_UNIT(mynor) ((((mynor) & ~0xffffU) >> (8 + 3)) \ | ((mynor) & 0x1f)) #define UNIT_TO_MINOR(unit) ((((unit) & ~0x1fU) << (8 + 3)) \ | ((unit) & 0x1f)) /* * Meaning of flags: * * 0x00000001 shared IRQs * 0x00000002 disable FIFO * 0x00000008 recover sooner from lost output interrupts * 0x00000010 device is potential system console * 0x00000020 device is forced to become system console * 0x00000040 device is reserved for low-level IO * 0x00000080 use this port for remote kernel debugging * 0x0000??00 minor number of master port * 0x00010000 PPS timestamping on CTS instead of DCD * 0x00080000 IIR_TXRDY bug * 0x00400000 If no comconsole found then mark as a comconsole * 0x1?000000 interface type */ #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(flags) ((flags) & 0x01) #define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) #ifndef PC98 #define COM_NOTAST4(flags) ((flags) & 0x04) #endif #else #define COM_ISMULTIPORT(flags) (0) #endif /* COM_MULTIPORT */ #define COM_C_IIR_TXRDYBUG 0x80000 #define COM_CONSOLE(flags) ((flags) & 0x10) #define COM_DEBUGGER(flags) ((flags) & 0x80) #ifndef PC98 #define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) #endif #define COM_FORCECONSOLE(flags) ((flags) & 0x20) #define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) #define COM_LLCONSOLE(flags) ((flags) & 0x40) #define COM_LOSESOUTINTS(flags) ((flags) & 0x08) #define COM_NOFIFO(flags) ((flags) & 0x02) #ifndef PC98 #define COM_NOSCR(flags) ((flags) & 0x100000) #endif #define COM_PPSCTS(flags) ((flags) & 0x10000) #ifndef PC98 #define COM_ST16650A(flags) ((flags) & 0x20000) #define COM_TI16754(flags) ((flags) & 0x200000) #endif #define sio_getreg(com, off) \ (bus_space_read_1((com)->bst, (com)->bsh, (off))) #define sio_setreg(com, off, value) \ (bus_space_write_1((com)->bst, (com)->bsh, (off), (value))) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * comstop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ bool_t st16650a; /* nonzero if Startech 16650A compatible */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int flags; /* copy of device flags */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ibufold; /* old input buffer, to be freed */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ int ibufsize; /* size of ibuf (not include error bytes) */ int ierroff; /* offset of error bytes in ibuf */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ bus_space_tag_t bst; bus_space_handle_t bsh; #ifdef PC98 Port_t cmd_port; Port_t sts_port; Port_t in_modem_port; Port_t intr_ctrl_port; Port_t rsabase; /* Iobase address of an I/O-DATA RSA board. */ int intr_enable; int pc98_prev_modem_status; int pc98_modem_delta; int modem_car_chg_timer; int pc98_prev_siocmd; int pc98_prev_siomod; int modem_checking; int pc98_if_type; bool_t pc98_8251fifo; bool_t pc98_8251fifo_enable; #endif /* PC98 */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_ctl_port; Port_t int_id_port; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; struct pps_state pps; int pps_bit; #ifdef ALT_BREAK_TO_DEBUGGER int alt_brk_state; #endif u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; u_long rclk; struct resource *irqres; struct resource *ioportres; int ioportrid; void *cookie; dev_t devs[6]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ #ifdef PC98 int obufsize; u_char *obuf1; u_char *obuf2; #else u_char obuf1[256]; u_char obuf2[256]; #endif }; #ifdef COM_ESP static int espattach(struct com_s *com, Port_t esp_port); #endif static timeout_t siobusycheck; static u_int siodivisor(u_long rclk, speed_t speed); static timeout_t siodtrwakeup; static void comhardclose(struct com_s *com); static void sioinput(struct com_s *com); static void siointr1(struct com_s *com); static void siointr(void *arg); static int commctl(struct com_s *com, int bits, int how); static int comparam(struct tty *tp, struct termios *t); static void siopoll(void *); static void siosettimeout(void); static int siosetwater(struct com_s *com, speed_t speed); static void comstart(struct tty *tp); static void comstop(struct tty *tp, int rw); static timeout_t comwakeup; char sio_driver_name[] = "sio"; static struct mtx sio_lock; static int sio_inited; /* table and macro for fast conversion from a unit number to its com struct */ devclass_t sio_devclass; #define com_addr(unit) ((struct com_s *) \ devclass_get_softc(sio_devclass, unit)) /* XXX */ static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static struct cdevsw sio_cdevsw = { .d_version = D_VERSION, .d_open = sioopen, .d_close = sioclose, .d_read = sioread, .d_write = siowrite, .d_ioctl = sioioctl, .d_name = sio_driver_name, .d_flags = D_TTY | D_NEEDGIANT, }; int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; static u_long comdefaultrclk = DEFAULT_RCLK; SYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, ""); static speed_t gdbdefaultrate = GDBSPEED; SYSCTL_UINT(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW, &gdbdefaultrate, GDBSPEED, ""); static u_int com_events; /* input chars + weighted output completions */ static Port_t siocniobase; static int siocnunit = -1; static Port_t siogdbiobase; static int siogdbunit = -1; static void *sio_slow_ih; static void *sio_fast_ih; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); static int sio_numunits; #ifdef PC98 struct siodev { short if_type; short irq; Port_t cmd, sts, ctrl, mod; }; static int sysclock; #define COM_INT_DISABLE {int previpri; previpri=spltty(); #define COM_INT_ENABLE splx(previpri);} #define IEN_TxFLAG IEN_Tx #define COM_CARRIER_DETECT_EMULATE 0 #define PC98_CHECK_MODEM_INTERVAL (hz/10) #define DCD_OFF_TOLERANCE 2 #define DCD_ON_RECOGNITION 2 #define IS_8251(if_type) (!(if_type & 0x10)) #define COM1_EXT_CLOCK 0x40000 static void commint(dev_t dev); static void com_tiocm_set(struct com_s *com, int msr); static void com_tiocm_bis(struct com_s *com, int msr); static void com_tiocm_bic(struct com_s *com, int msr); static int com_tiocm_get(struct com_s *com); static int com_tiocm_get_delta(struct com_s *com); static void pc98_msrint_start(dev_t dev); static void com_cflag_and_speed_set(struct com_s *com, int cflag, int speed); static int pc98_ttspeedtab(struct com_s *com, int speed, u_int *divisor); static int pc98_get_modem_status(struct com_s *com); static timeout_t pc98_check_msr; static void pc98_set_baud_rate(struct com_s *com, u_int count); static void pc98_i8251_reset(struct com_s *com, int mode, int command); static void pc98_disable_i8251_interrupt(struct com_s *com, int mod); static void pc98_enable_i8251_interrupt(struct com_s *com, int mod); static int pc98_check_i8251_interrupt(struct com_s *com); static int pc98_i8251_get_cmd(struct com_s *com); static int pc98_i8251_get_mod(struct com_s *com); static void pc98_i8251_set_cmd(struct com_s *com, int x); static void pc98_i8251_or_cmd(struct com_s *com, int x); static void pc98_i8251_clear_cmd(struct com_s *com, int x); static void pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x); static int pc98_check_if_type(device_t dev, struct siodev *iod); static int pc98_check_8251vfast(void); static int pc98_check_8251fifo(void); static void pc98_check_sysclock(void); static void pc98_set_ioport(struct com_s *com); #define com_int_Tx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP) #define com_int_Tx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_TxFLAG) #define com_int_Rx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Rx) #define com_int_Rx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_Rx) #define com_int_TxRx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP|IEN_Rx) #define com_int_TxRx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_TxFLAG|IEN_Rx) #define com_send_break_on(com) \ pc98_i8251_or_cmd(com,CMD8251_SBRK) #define com_send_break_off(com) \ pc98_i8251_clear_cmd(com,CMD8251_SBRK) static struct speedtab pc98speedtab[] = { /* internal RS232C interface */ { 0, 0, }, { 50, 50, }, { 75, 75, }, { 150, 150, }, { 200, 200, }, { 300, 300, }, { 600, 600, }, { 1200, 1200, }, { 2400, 2400, }, { 4800, 4800, }, { 9600, 9600, }, { 19200, 19200, }, { 38400, 38400, }, { 51200, 51200, }, { 76800, 76800, }, { 20800, 20800, }, { 31200, 31200, }, { 41600, 41600, }, { 62400, 62400, }, { -1, -1 } }; static struct speedtab pc98fast_speedtab[] = { { 9600, 0x80 | (DEFAULT_RCLK / (16 * (9600))), }, { 19200, 0x80 | (DEFAULT_RCLK / (16 * (19200))), }, { 38400, 0x80 | (DEFAULT_RCLK / (16 * (38400))), }, { 57600, 0x80 | (DEFAULT_RCLK / (16 * (57600))), }, { 115200, 0x80 | (DEFAULT_RCLK / (16 * (115200))), }, { -1, -1 } }; static struct speedtab comspeedtab_pio9032b[] = { { 300, 6, }, { 600, 5, }, { 1200, 4, }, { 2400, 3, }, { 4800, 2, }, { 9600, 1, }, { 19200, 0, }, { 38400, 7, }, { -1, -1 } }; static struct speedtab comspeedtab_b98_01[] = { { 75, 11, }, { 150, 10, }, { 300, 9, }, { 600, 8, }, { 1200, 7, }, { 2400, 6, }, { 4800, 5, }, { 9600, 4, }, { 19200, 3, }, { 38400, 2, }, { 76800, 1, }, { 153600, 0, }, { -1, -1 } }; static struct speedtab comspeedtab_ind[] = { { 300, 1536, }, { 600, 768, }, { 1200, 384, }, { 2400, 192, }, { 4800, 96, }, { 9600, 48, }, { 19200, 24, }, { 38400, 12, }, { 57600, 8, }, { 115200, 4, }, { 153600, 3, }, { 230400, 2, }, { 460800, 1, }, { -1, -1 } }; struct { char *name; short port_table[7]; short irr_mask; struct speedtab *speedtab; short check_irq; } if_8251_type[] = { /* COM_IF_INTERNAL */ { " (internal)", {0x30, 0x32, 0x32, 0x33, 0x35, -1, -1}, -1, pc98speedtab, 1 }, /* COM_IF_PC9861K_1 */ { " (PC9861K)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, -1, -1}, 3, NULL, 1 }, /* COM_IF_PC9861K_2 */ { " (PC9861K)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, -1, -1}, 3, NULL, 1 }, /* COM_IF_IND_SS_1 */ { " (IND-SS)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb3, -1}, 3, comspeedtab_ind, 1 }, /* COM_IF_IND_SS_2 */ { " (IND-SS)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xbb, -1}, 3, comspeedtab_ind, 1 }, /* COM_IF_PIO9032B_1 */ { " (PIO9032B)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb8, -1}, 7, comspeedtab_pio9032b, 1 }, /* COM_IF_PIO9032B_2 */ { " (PIO9032B)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xba, -1}, 7, comspeedtab_pio9032b, 1 }, /* COM_IF_B98_01_1 */ { " (B98-01)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xd1, 0xd3}, 7, comspeedtab_b98_01, 0 }, /* COM_IF_B98_01_2 */ { " (B98-01)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xd5, 0xd7}, 7, comspeedtab_b98_01, 0 }, }; #define PC98SIO_data_port(type) (if_8251_type[type].port_table[0]) #define PC98SIO_cmd_port(type) (if_8251_type[type].port_table[1]) #define PC98SIO_sts_port(type) (if_8251_type[type].port_table[2]) #define PC98SIO_in_modem_port(type) (if_8251_type[type].port_table[3]) #define PC98SIO_intr_ctrl_port(type) (if_8251_type[type].port_table[4]) #define PC98SIO_baud_rate_port(type) (if_8251_type[type].port_table[5]) #define PC98SIO_func_port(type) (if_8251_type[type].port_table[6]) #define I8251F_data 0x130 #define I8251F_lsr 0x132 #define I8251F_msr 0x134 #define I8251F_iir 0x136 #define I8251F_fcr 0x138 #define I8251F_div 0x13a static bus_addr_t port_table_0[] = {0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007}; static bus_addr_t port_table_1[] = {0x000, 0x002, 0x004, 0x006, 0x008, 0x00a, 0x00c, 0x00e}; static bus_addr_t port_table_8[] = {0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700}; static bus_addr_t port_table_rsa[] = { 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007 }; struct { char *name; short irr_read; short irr_write; bus_addr_t *iat; bus_size_t iatsz; u_long rclk; } if_16550a_type[] = { /* COM_IF_RSA98 */ {" (RSA-98)", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, /* COM_IF_NS16550 */ {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, /* COM_IF_SECOND_CCU */ {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, /* COM_IF_MC16550II */ {" (MC16550II)", -1, 0x1000, port_table_8, IO_COMSIZE, DEFAULT_RCLK * 4}, /* COM_IF_MCRS98 */ {" (MC-RS98)", -1, 0x1000, port_table_8, IO_COMSIZE, DEFAULT_RCLK * 4}, /* COM_IF_RSB3000 */ {" (RSB-3000)", 0xbf, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 10}, /* COM_IF_RSB384 */ {" (RSB-384)", 0xbf, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 10}, /* COM_IF_MODEM_CARD */ {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, /* COM_IF_RSA98III */ {" (RSA-98III)", -1, -1, port_table_rsa, 16, DEFAULT_RCLK * 8}, /* COM_IF_ESP98 */ {" (ESP98)", -1, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 4}, }; #endif /* PC98 */ #ifdef COM_ESP #ifdef PC98 /* XXX configure this properly. */ /* XXX quite broken for new-bus. */ static Port_t likely_com_ports[] = { 0, 0xb0, 0xb1, 0 }; static Port_t likely_esp_ports[] = { 0xc0d0, 0 }; #define ESP98_CMD1 (ESP_CMD1 * 0x100) #define ESP98_CMD2 (ESP_CMD2 * 0x100) #define ESP98_STATUS1 (ESP_STATUS1 * 0x100) #define ESP98_STATUS2 (ESP_STATUS2 * 0x100) #else /* PC98 */ /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif /* PC98 */ #endif /* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */ static int sysctl_machdep_comdefaultrate(SYSCTL_HANDLER_ARGS) { int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (com == NULL) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error; } SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", ""); /* * Unload the driver and clear the table. * XXX this is mostly wrong. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a kldunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ int siodetach(dev) device_t dev; { struct com_s *com; int i; com = (struct com_s *) device_get_softc(dev); if (com == NULL) { device_printf(dev, "NULL com in siounload\n"); return (0); } com->gone = TRUE; for (i = 0 ; i < 6; i++) destroy_dev(com->devs[i]); if (com->irqres) { bus_teardown_intr(dev, com->irqres, com->cookie); bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres); } if (com->ioportres) bus_release_resource(dev, SYS_RES_IOPORT, com->ioportrid, com->ioportres); if (com->tp && (com->tp->t_state & TS_ISOPEN)) { device_printf(dev, "still open, forcing close\n"); ttyld_close(com->tp, 0); ttyclose(com->tp); } else { if (com->ibuf != NULL) free(com->ibuf, M_DEVBUF); #ifdef PC98 if (com->obuf1 != NULL) free(com->obuf1, M_DEVBUF); #endif device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (0); } int sioprobe(dev, xrid, rclk, noprobe) device_t dev; int xrid; u_long rclk; int noprobe; { #if 0 static bool_t already_init; device_t xdev; #endif struct com_s *com; u_int divisor; bool_t failures[10]; int fn; device_t idev; Port_t iobase; intrmask_t irqmap[4]; intrmask_t irqs; u_char mcr_image; int result; u_long xirq; u_int flags = device_get_flags(dev); int rid; struct resource *port; #ifdef PC98 int tmp; struct siodev iod; #endif #ifdef PC98 iod.if_type = GET_IFTYPE(flags); if ((iod.if_type < 0 || iod.if_type > COM_IF_END1) && (iod.if_type < 0x10 || iod.if_type > COM_IF_END2)) return ENXIO; #endif rid = xrid; #ifdef PC98 if (IS_8251(iod.if_type)) { port = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); } else if (iod.if_type == COM_IF_MODEM_CARD || iod.if_type == COM_IF_RSA98III || isa_get_vendorid(dev)) { port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, if_16550a_type[iod.if_type & 0x0f].iatsz, RF_ACTIVE); } else { port = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, if_16550a_type[iod.if_type & 0x0f].iat, if_16550a_type[iod.if_type & 0x0f].iatsz, RF_ACTIVE); } #else port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_COMSIZE, RF_ACTIVE); #endif if (!port) return (ENXIO); #ifdef PC98 if (!IS_8251(iod.if_type)) { if (isa_load_resourcev(port, if_16550a_type[iod.if_type & 0x0f].iat, if_16550a_type[iod.if_type & 0x0f].iatsz) != 0) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return ENXIO; } } #endif com = malloc(sizeof(*com), M_DEVBUF, M_NOWAIT | M_ZERO); if (com == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return (ENOMEM); } device_set_softc(dev, com); com->bst = rman_get_bustag(port); com->bsh = rman_get_bushandle(port); #ifdef PC98 if (!IS_8251(iod.if_type) && rclk == 0) rclk = if_16550a_type[iod.if_type & 0x0f].rclk; #else if (rclk == 0) rclk = DEFAULT_RCLK; #endif com->rclk = rclk; while (sio_inited != 2) if (atomic_cmpset_int(&sio_inited, 0, 1)) { mtx_init(&sio_lock, sio_driver_name, NULL, (comconsole != -1) ? MTX_SPIN | MTX_QUIET : MTX_SPIN); atomic_store_rel_int(&sio_inited, 2); } #if 0 /* * XXX this is broken - when we are first called, there are no * previously configured IO ports. We could hard code * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse. * This code has been doing nothing since the conversion since * "count" is zero the first time around. */ if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ device_t *devs; int count, i, xioport; #ifdef PC98 int xiftype; #endif devclass_get_devices(sio_devclass, &devs, &count); #ifdef PC98 for (i = 0; i < count; i++) { xdev = devs[i]; xioport = bus_get_resource_start(xdev, SYS_RES_IOPORT, 0); xiftype = GET_IFTYPE(device_get_flags(xdev)); if (device_is_enabled(xdev) && xioport > 0) { if (IS_8251(xiftype)) outb((xioport & 0xff00) | PC98SIO_cmd_port(xiftype & 0x0f), 0xf2); else outb(xioport + if_16550a_type[xiftype & 0x0f].iat[com_mcr], 0); } } #else for (i = 0; i < count; i++) { xdev = devs[i]; if (device_is_enabled(xdev) && bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport, NULL) == 0) outb(xioport + com_mcr, 0); } #endif free(devs, M_TEMP); already_init = TRUE; } #endif if (COM_LLCONSOLE(flags)) { printf("sio%d: reserved for low-level i/o\n", device_get_unit(dev)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } #ifdef PC98 DELAY(10); /* * If the port is i8251 UART (internal, B98_01) */ if (pc98_check_if_type(dev, &iod) == -1) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } if (iod.irq > 0) bus_set_resource(dev, SYS_RES_IRQ, 0, iod.irq, 1); if (IS_8251(iod.if_type)) { outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, CMD8251_RESET); DELAY(1000); /* for a while...*/ outb(iod.cmd, 0xf2); /* MODE (dummy) */ DELAY(10); outb(iod.cmd, 0x01); /* CMD (dummy) */ DELAY(1000); /* for a while...*/ if (( inb(iod.sts) & STS8251_TxEMP ) == 0 ) { result = (ENXIO); } if (if_8251_type[iod.if_type & 0x0f].check_irq) { COM_INT_DISABLE tmp = ( inb( iod.ctrl ) & ~(IEN_Rx|IEN_TxEMP|IEN_Tx)); outb( iod.ctrl, tmp|IEN_TxEMP ); DELAY(10); result = isa_irq_pending() ? 0 : ENXIO; outb( iod.ctrl, tmp ); COM_INT_ENABLE } else { /* * B98_01 doesn't activate TxEMP interrupt line * when being reset, so we can't check irq pending. */ result = 0; } if (epson_machine_id==0x20) { /* XXX */ result = 0; } bus_release_resource(dev, SYS_RES_IOPORT, rid, port); if (result) { device_set_softc(dev, NULL); free(com, M_DEVBUF); } return result; } #endif /* PC98 */ /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { #ifndef PC98 Port_t xiobase; u_long io; #endif idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", device_get_unit(dev), COM_MPMASTER(flags)); idev = dev; } #ifndef PC98 if (!COM_NOTAST4(flags)) { if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io, NULL) == 0) { xiobase = io; if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) == 0) outb(xiobase + com_scr, 0x80); else outb(xiobase + com_scr, 0); } mcr_image = 0; } #endif } #endif /* COM_MULTIPORT */ if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = rman_get_start(port); #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) { mcr_image = 0; outb(iobase + rsa_msr, 0x04); outb(iobase + rsa_frr, 0x00); if ((inb(iobase + rsa_srr) & 0x36) != 0x36) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } outb(iobase + rsa_ier, 0x00); outb(iobase + rsa_frr, 0x00); outb(iobase + rsa_tivsr, 0x00); outb(iobase + rsa_tcr, 0x00); } tmp = if_16550a_type[iod.if_type & 0x0f].irr_write; if (tmp != -1) { /* MC16550II */ int irqout; switch (isa_get_irq(idev)) { case 3: irqout = 4; break; case 5: irqout = 5; break; case 6: irqout = 6; break; case 12: irqout = 7; break; default: printf("sio%d: irq configuration error\n", device_get_unit(dev)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); device_set_softc(dev, NULL); free(com, M_DEVBUF); return (ENXIO); } outb((iobase & 0x00ff) | tmp, irqout); } #endif /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ mtx_lock_spin(&sio_lock); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ if (iobase == siocniobase) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS); divisor = siodivisor(rclk, SIO_TEST_SPEED); sio_setreg(com, com_dlbl, divisor & 0xff); sio_setreg(com, com_dlbh, divisor >> 8); sio_setreg(com, com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image); sio_setreg(com, com_ier, 0); DELAY(1000); /* XXX */ irqmap[0] = isa_irq_pending(); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ sio_setreg(com, com_ier, IER_ETXRDY); #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) outb(iobase + rsa_ier, 0x04); #endif /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ sio_setreg(com, com_data, 0); if (iobase == siocniobase) DELAY((1 + 2) * 1000000 / (comdefaultrate / 10)); else DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ sio_setreg(com, com_mcr, mcr_image); /* * It seems my Xircom CBEM56G Cardbus modem wants to be reset * to 8 bits *again*, or else probe test 0 will fail. * gwk@sgi.com, 4/19/2001 */ sio_setreg(com, com_cfcr, CFCR_8BITS); /* * Some PCMCIA cards (Palido 321s, DC-1S, ...) have the "TXRDY bug", * so we probe for a buggy IIR_TXRDY implementation even in the * noprobe case. We don't probe for it in the !noprobe case because * noprobe is always set for PCMCIA cards and the problem is not * known to affect any other cards. */ if (noprobe) { /* Read IIR a few times. */ for (fn = 0; fn < 2; fn ++) { DELAY(10000); failures[6] = sio_getreg(com, com_iir); } /* IIR_TXRDY should be clear. Is it? */ result = 0; if (failures[6] & IIR_TXRDY) { /* * No. We seem to have the bug. Does our fix for * it work? */ sio_setreg(com, com_ier, 0); if (sio_getreg(com, com_iir) & IIR_NOPEND) { /* Yes. We discovered the TXRDY bug! */ SET_FLAG(dev, COM_C_IIR_TXRDYBUG); } else { /* No. Just fail. XXX */ result = ENXIO; sio_setreg(com, com_mcr, 0); } } else { /* Yes. No bug. */ CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); } sio_setreg(com, com_ier, 0); sio_setreg(com, com_cfcr, CFCR_8BITS); mtx_unlock_spin(&sio_lock); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); if (iobase == siocniobase) result = 0; if (result != 0) { device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (result); } /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS; failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY; failures[2] = sio_getreg(com, com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ irqmap[1] = isa_irq_pending(); failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY; #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) inb(iobase + rsa_srr); #endif DELAY(1000); /* XXX */ irqmap[2] = isa_irq_pending(); failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) inb(iobase + rsa_srr); #endif /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving it) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ sio_setreg(com, com_ier, 0); sio_setreg(com, com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = sio_getreg(com, com_ier); #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) outb(iobase + rsa_ier, 0x00); #endif DELAY(1000); /* XXX */ irqmap[3] = isa_irq_pending(); failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; #ifdef PC98 if (iod.if_type == COM_IF_RSA98III) { inb(iobase + rsa_srr); outb(iobase + rsa_frr, 0x00); } #endif mtx_unlock_spin(&sio_lock); irqs = irqmap[1] & ~irqmap[0]; if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 && ((1 << xirq) & irqs) == 0) { printf( "sio%d: configured irq %ld not in bitmap of probed irqs %#x\n", device_get_unit(dev), xirq, irqs); printf( "sio%d: port may not be enabled\n", device_get_unit(dev)); } if (bootverbose) printf("sio%d: irq maps: %#x %#x %#x %#x\n", device_get_unit(dev), irqmap[0], irqmap[1], irqmap[2], irqmap[3]); result = 0; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { sio_setreg(com, com_mcr, 0); result = ENXIO; if (bootverbose) { printf("sio%d: probe failed test(s):", device_get_unit(dev)); for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) printf(" %d", fn); printf("\n"); } break; } bus_release_resource(dev, SYS_RES_IOPORT, rid, port); if (iobase == siocniobase) result = 0; if (result != 0) { device_set_softc(dev, NULL); free(com, M_DEVBUF); } return (result); } #ifdef COM_ESP static int espattach(com, esp_port) struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ #ifdef PC98 outb(esp_port + ESP98_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP98_STATUS1); #else outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); #endif /* * Bits 0,1 of dips say which COM port we are. */ #ifdef PC98 if ((rman_get_start(com->ioportres) & 0xff) == likely_com_ports[dips & 0x03]) #else if (rman_get_start(com->ioportres) == likely_com_ports[dips & 0x03]) #endif printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ #ifdef PC98 outb(esp_port + ESP98_CMD1, ESP_GETTEST); val = inb(esp_port + ESP98_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP98_STATUS2); #else outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); #endif if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ int sioattach(dev, xrid, rclk) device_t dev; int xrid; u_long rclk; { struct com_s *com; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int minorbase; int unit; u_int flags; int rid; struct resource *port; int ret; #ifdef PC98 u_char *obuf; u_long obufsize; int if_type = GET_IFTYPE(device_get_flags(dev)); #endif rid = xrid; #ifdef PC98 if (IS_8251(if_type)) { port = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); } else if (if_type == COM_IF_MODEM_CARD || if_type == COM_IF_RSA98III || isa_get_vendorid(dev)) { port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, if_16550a_type[if_type & 0x0f].iatsz, RF_ACTIVE); } else { port = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, if_16550a_type[if_type & 0x0f].iat, if_16550a_type[if_type & 0x0f].iatsz, RF_ACTIVE); } #else port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_COMSIZE, RF_ACTIVE); #endif if (!port) return (ENXIO); #ifdef PC98 if (!IS_8251(if_type)) { if (isa_load_resourcev(port, if_16550a_type[if_type & 0x0f].iat, if_16550a_type[if_type & 0x0f].iatsz) != 0) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return ENXIO; } } #endif iobase = rman_get_start(port); unit = device_get_unit(dev); com = device_get_softc(dev); flags = device_get_flags(dev); if (unit >= sio_numunits) sio_numunits = unit + 1; #ifdef PC98 obufsize = 256; if (if_type == COM_IF_RSA98III) obufsize = 2048; if ((obuf = malloc(obufsize * 2, M_DEVBUF, M_NOWAIT)) == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return ENXIO; } bzero(obuf, obufsize * 2); #endif /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->ioportres = port; com->ioportrid = rid; com->bst = rman_get_bustag(port); com->bsh = rman_get_bushandle(port); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(flags) != 0; com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; com->tx_fifo_size = 1; #ifdef PC98 com->obufsize = obufsize; com->obuf1 = obuf; com->obuf2 = obuf + obufsize; #endif com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; #ifdef PC98 com->pc98_if_type = if_type; if (IS_8251(if_type)) { pc98_set_ioport(com); if (if_type == COM_IF_INTERNAL && pc98_check_8251fifo()) { com->pc98_8251fifo = 1; com->pc98_8251fifo_enable = 0; } } else { bus_addr_t *iat = if_16550a_type[if_type & 0x0f].iat; com->data_port = iobase + iat[com_data]; com->int_ctl_port = iobase + iat[com_ier]; com->int_id_port = iobase + iat[com_iir]; com->modem_ctl_port = iobase + iat[com_mcr]; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + iat[com_lsr]; com->modem_status_port = iobase + iat[com_msr]; } #else /* not PC98 */ com->data_port = iobase + com_data; com->int_ctl_port = iobase + com_ier; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; #endif #ifdef PC98 if (!IS_8251(if_type) && rclk == 0) rclk = if_16550a_type[if_type & 0x0f].rclk; #else if (rclk == 0) rclk = DEFAULT_RCLK; #endif com->rclk = rclk; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { #ifdef PC98 if (IS_8251(com->pc98_if_type)) DELAY(100000); #endif com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; com->lt_out.c_ispeed = com->lt_out.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; if (siosetwater(com, com->it_in.c_ispeed) != 0) { mtx_unlock_spin(&sio_lock); /* * Leave i/o resources allocated if this is a `cn'-level * console, so that other devices can't snarf them. */ if (iobase != siocniobase) bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return (ENOMEM); } mtx_unlock_spin(&sio_lock); termioschars(&com->it_in); com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifndef PC98 if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags) && !COM_NOSCR(flags)) { u_char scr; u_char scr1; u_char scr2; scr = sio_getreg(com, com_scr); sio_setreg(com, com_scr, 0xa5); scr1 = sio_getreg(com, com_scr); sio_setreg(com, com_scr, 0x5a); scr2 = sio_getreg(com, com_scr); sio_setreg(com, com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250 or not responding"); goto determined_type; } } #endif /* !PC98 */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (com->pc98_8251fifo && !COM_NOFIFO(flags)) com->tx_fifo_size = 16; com_int_TxRx_disable( com ); com_cflag_and_speed_set( com, com->it_in.c_cflag, comdefaultrate ); com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); com_send_break_off( com ); if (com->pc98_if_type == COM_IF_INTERNAL) { printf(" (internal%s%s)", com->pc98_8251fifo ? " fifo" : "", PC98SIO_baud_rate_port(com->pc98_if_type) != -1 ? " v-fast" : ""); } else { printf(" 8251%s", if_8251_type[com->pc98_if_type & 0x0f].name); } } else { #endif /* PC98 */ sio_setreg(com, com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: if (COM_NOFIFO(flags)) { printf(" 16550A fifo disabled"); break; } com->hasfifo = TRUE; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) { com->tx_fifo_size = 2048; com->rsabase = iobase; outb(com->rsabase + rsa_ier, 0x00); outb(com->rsabase + rsa_frr, 0x00); } #else if (COM_ST16650A(flags)) { printf(" ST16650A"); com->st16650a = TRUE; com->tx_fifo_size = 32; break; } if (COM_TI16754(flags)) { printf(" TI16754"); com->tx_fifo_size = 64; break; } #endif printf(" 16550A"); #ifdef COM_ESP #ifdef PC98 if (com->pc98_if_type == COM_IF_ESP98) #endif for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(com, *espp)) { com->tx_fifo_size = 1024; break; } if (com->esp) break; #endif #ifdef PC98 com->tx_fifo_size = 16; #else com->tx_fifo_size = COM_FIFOSIZE(flags); if (com->tx_fifo_size == 0) com->tx_fifo_size = 16; else printf(" lookalike with %u bytes FIFO", com->tx_fifo_size); #endif break; } #ifdef PC98 if (com->pc98_if_type == COM_IF_RSB3000) { /* Set RSB-2000/3000 Extended Buffer mode. */ u_char lcr; lcr = sio_getreg(com, com_cfcr); sio_setreg(com, com_cfcr, lcr | CFCR_DLAB); sio_setreg(com, com_emr, EMR_EXBUFF | EMR_EFMODE); sio_setreg(com, com_cfcr, lcr); } #endif #ifdef COM_ESP if (com->esp) { /* * Set 16550 compatibility mode. * We don't use the ESP_MODE_SCALE bit to increase the * fifo trigger levels because we can't handle large * bursts of input. * XXX flow control should be set in comparam(), not here. */ #ifdef PC98 outb(com->esp_port + ESP98_CMD1, ESP_SETMODE); outb(com->esp_port + ESP98_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); #else outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); #endif /* Set RTS/CTS flow control. */ #ifdef PC98 outb(com->esp_port + ESP98_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP98_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP98_CMD2, ESP_FLOW_CTS); #else outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); #endif /* Set flow-control levels. */ #ifdef PC98 outb(com->esp_port + ESP98_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP98_CMD2, HIBYTE(768)); outb(com->esp_port + ESP98_CMD2, LOBYTE(768)); outb(com->esp_port + ESP98_CMD2, HIBYTE(512)); outb(com->esp_port + ESP98_CMD2, LOBYTE(512)); #else outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); #endif #ifdef PC98 /* Set UART clock prescaler. */ outb(com->esp_port + ESP98_CMD1, ESP_SETCLOCK); outb(com->esp_port + ESP98_CMD2, 2); /* 4 times */ #endif } #endif /* COM_ESP */ sio_setreg(com, com_fifo, 0); #ifdef PC98 printf("%s", if_16550a_type[com->pc98_if_type & 0x0f].name); #else determined_type: ; #endif #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { device_t masterdev; com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(flags)) printf(" master"); printf(")"); masterdev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); com->no_irq = (masterdev == NULL || bus_get_resource(masterdev, SYS_RES_IRQ, 0, NULL, NULL) != 0); } #endif /* COM_MULTIPORT */ #ifdef PC98 } #endif if (unit == comconsole) printf(", console"); if (COM_IIR_TXRDYBUG(flags)) printf(" with a buggy IIR_TXRDY implementation"); printf("\n"); if (sio_fast_ih == NULL) { swi_add(&tty_ithd, "tty:sio", siopoll, NULL, SWI_TTY, 0, &sio_fast_ih); swi_add(&clk_ithd, "tty:sio", siopoll, NULL, SWI_TTY, 0, &sio_slow_ih); } minorbase = UNIT_TO_MINOR(unit); com->devs[0] = make_dev(&sio_cdevsw, minorbase, UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); com->devs[1] = make_dev(&sio_cdevsw, minorbase | CONTROL_INIT_STATE, UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); com->devs[2] = make_dev(&sio_cdevsw, minorbase | CONTROL_LOCK_STATE, UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); com->devs[3] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK, UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); com->devs[4] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_INIT_STATE, UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); com->devs[5] = make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_LOCK_STATE, UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); for (rid = 0; rid < 6; rid++) com->devs[rid]->si_drv1 = com; com->flags = flags; com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; if (COM_PPSCTS(flags)) com->pps_bit = MSR_CTS; else com->pps_bit = MSR_DCD; pps_init(&com->pps); rid = 0; com->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (com->irqres) { ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, INTR_TYPE_TTY | INTR_FAST, siointr, com, &com->cookie); if (ret) { ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, INTR_TYPE_TTY, siointr, com, &com->cookie); if (ret == 0) device_printf(dev, "unable to activate interrupt in fast mode - using normal mode\n"); } if (ret) device_printf(dev, "could not activate interrupt\n"); #if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ defined(ALT_BREAK_TO_DEBUGGER)) /* * Enable interrupts for early break-to-debugger support * on the console. */ if (ret == 0 && unit == comconsole) outb(siocniobase + com_ier, IER_ERXRDY | IER_ERLS | IER_EMSC); #endif } return (0); } static int sioopen(dev, flag, mode, td) dev_t dev; int flag; int mode; struct thread *td; { struct com_s *com; int error; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); if (com == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); tp = dev->si_tty = com->tp = ttymalloc(com->tp); s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && suser(td)) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_stop = comstop; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; #ifdef PC98 if (!IS_8251(com->pc98_if_type)) #endif (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; #ifdef PC98 if (IS_8251(com->pc98_if_type)) { com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS); pc98_msrint_start(dev); if (com->pc98_8251fifo) { com->pc98_8251fifo_enable = 1; outb(I8251F_fcr, CTRL8251F_ENABLE | CTRL8251F_XMT_RST | CTRL8251F_RCV_RST); } } #endif /* * XXX we should goto open_top if comparam() slept. */ if (com->hasfifo) { int i; /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ for (i = 0; i < 500; i++) { sio_setreg(com, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) outb(com->rsabase + rsa_frr , 0x00); #endif /* * XXX the delays are for superstitious * historical reasons. It must be less than * the character time at the maximum * supported speed (87 usec at 115200 bps * 8N1). Otherwise we might loop endlessly * if data is streaming in. We used to use * delays of 100. That usually worked * because DELAY(100) used to usually delay * for about 85 usec instead of 100. */ DELAY(50); #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III ? !(inb(com->rsabase + rsa_srr) & 0x08) : !(inb(com->line_status_port) & LSR_RXRDY)) break; #else if (!(inb(com->line_status_port) & LSR_RXRDY)) break; #endif sio_setreg(com, com_fifo, 0); DELAY(50); (void) inb(com->data_port); } if (i == 500) { error = EIO; goto out; } } mtx_lock_spin(&sio_lock); #ifdef PC98 if (IS_8251(com->pc98_if_type)) { com_tiocm_bis(com, TIOCM_LE); com->pc98_prev_modem_status = pc98_get_modem_status(com); com_int_Rx_enable(com); } else { #endif (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(com->int_ctl_port, IER_ERXRDY | IER_ERLS | IER_EMSC | (COM_IIR_TXRDYBUG(com->flags) ? 0 : IER_ETXRDY)); #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) { outb(com->rsabase + rsa_ier, 0x1d); outb(com->int_ctl_port, IER_ERLS | IER_EMSC); } #endif #ifdef PC98 } #endif mtx_unlock_spin(&sio_lock); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ #ifdef PC98 if ((IS_8251(com->pc98_if_type) && (pc98_get_modem_status(com) & TIOCM_CAR)) || (!IS_8251(com->pc98_if_type) && (com->prev_modem_status & MSR_DCD)) || mynor & CALLOUT_MASK) ttyld_modem(tp, 1); #else if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) ttyld_modem(tp, 1); #endif } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = ttyld_open(tp, dev); com->hotchar = ttyldoptim(tp); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, td) dev_t dev; int flag; int mode; struct thread *td; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL) return (ENODEV); tp = com->tp; s = spltty(); ttyld_close(tp, flag); #ifdef PC98 com->modem_checking = 0; #endif com->hotchar = ttyldoptim(tp); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); if (com->ibuf != NULL) free(com->ibuf, M_DEVBUF); bzero(tp, sizeof *tp); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { int s; struct tty *tp; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = FALSE; com->do_dcd_timestamp = FALSE; com->pps.ppsparam.mode = 0; #ifdef PC98 if (IS_8251(com->pc98_if_type)) com_send_break_off(com); else #endif sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); tp = com->tp; #if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ defined(ALT_BREAK_TO_DEBUGGER)) /* * Leave interrupts enabled and don't clear DTR if this is the * console. This allows us to detect break-to-debugger events * while the console device is closed. */ if (com->unit != comconsole) #endif { #ifdef PC98 int tmp; if (IS_8251(com->pc98_if_type)) com_int_TxRx_disable(com); else sio_setreg(com, com_ier, 0); if (com->pc98_if_type == COM_IF_RSA98III) outb(com->rsabase + rsa_ier, 0x00); if (IS_8251(com->pc98_if_type)) tmp = pc98_get_modem_status(com) & TIOCM_CAR; else tmp = com->prev_modem_status & MSR_DCD; #else sio_setreg(com, com_ier, 0); #endif if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || (!com->active_out #ifdef PC98 && !(tmp) #else && !(com->prev_modem_status & MSR_DCD) #endif && !(com->it_in.c_cflag & CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { #ifdef PC98 if (IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); else #endif (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } #ifdef PC98 else { if (IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_LE); } #endif } #ifdef PC98 if (com->pc98_8251fifo) { if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_XMT_RST | CTRL8251F_RCV_RST); com->pc98_8251fifo_enable = 0; } #endif if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ sio_setreg(com, com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct com_s *com; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL || com->gone) return (ENODEV); return (ttyld_read(com->tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct com_s *com; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); if (com == NULL || com->gone) return (ENODEV); /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return (ttyld_write(com->tp, uio, flag)); } static void siobusycheck(chan) void *chan; { struct com_s *com; int s; com = (struct com_s *)chan; /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. * If siointr1() clears CS_BUSY after we look at it, then we'll get * called again. Reading the line status port outside of siointr1() * is safe because CS_BUSY is clear so there are no output interrupts * to lose. */ s = spltty(); if (com->state & CS_BUSY) com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ #ifdef PC98 else if ((IS_8251(com->pc98_if_type) && ((com->pc98_8251fifo_enable && (inb(I8251F_lsr) & (STS8251F_TxRDY | STS8251F_TxEMP)) == (STS8251F_TxRDY | STS8251F_TxEMP)) || (!com->pc98_8251fifo_enable && (inb(com->sts_port) & (STS8251_TxRDY | STS8251_TxEMP)) == (STS8251_TxRDY | STS8251_TxEMP)))) || ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY))) { #else else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY)) { #endif com->tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); com->extra_state &= ~CSE_BUSYCHECK; } else timeout(siobusycheck, com, hz / 100); splx(s); } static u_int siodivisor(rclk, speed) u_long rclk; speed_t speed; { long actual_speed; u_int divisor; int error; if (speed == 0) return (0); #if UINT_MAX > (ULONG_MAX - 1) / 8 if (speed > (ULONG_MAX - 1) / 8) return (0); #endif divisor = (rclk / (8UL * speed) + 1) / 2; if (divisor == 0 || divisor >= 65536) return (0); actual_speed = rclk / (16UL * divisor); /* 10 times error in percent: */ error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2; /* 3.0% maximum error tolerance: */ if (error < -30 || error > 30) return (0); return (divisor); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } /* * Call this function with the sio_lock mutex held. It will return with the * lock still held. */ static void sioinput(com) struct com_s *com; { u_char *buf; int incc; u_char line_status; int recv_data; struct tty *tp; buf = com->ibuf; tp = com->tp; if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; return; } if (tp->t_state & TS_CAN_BYPASS_L_RINT) { /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ do { /* * This may look odd, but it is using save-and-enable * semantics instead of the save-and-disable semantics * that are used everywhere else. */ mtx_unlock_spin(&sio_lock); incc = com->iptr - buf; if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); buf += incc; tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } mtx_lock_spin(&sio_lock); } while (buf < com->iptr); } else { do { /* * This may look odd, but it is using save-and-enable * semantics instead of the save-and-disable semantics * that are used everywhere else. */ mtx_unlock_spin(&sio_lock); line_status = buf[com->ierroff]; recv_data = *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } ttyld_rint(tp, recv_data); mtx_lock_spin(&sio_lock); } while (buf < com->iptr); } com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; /* * There is now room for another low-level buffer full of input, * so enable RTS if it is now disabled and there is room in the * high-level buffer. */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if ((com->state & CS_RTS_IFLOW) && !(com_tiocm_get(com) & TIOCM_RTS) && !(tp->t_state & TS_TBLOCK)) com_tiocm_bis(com, TIOCM_RTS); } else { if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } #else if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #endif } static void siointr(arg) void *arg; { struct com_s *com; #if defined(PC98) && defined(COM_MULTIPORT) u_char rsa_buf_status; #endif #ifndef COM_MULTIPORT com = (struct com_s *)arg; mtx_lock_spin(&sio_lock); siointr1(com); mtx_unlock_spin(&sio_lock); #else /* COM_MULTIPORT */ bool_t possibly_more_intrs; int unit; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ mtx_lock_spin(&sio_lock); do { possibly_more_intrs = FALSE; for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); /* * XXX COM_LOCK(); * would it work here, or be counter-productive? */ #ifdef PC98 if (com != NULL && !com->gone && IS_8251(com->pc98_if_type)) { siointr1(com); } else if (com != NULL && !com->gone && com->pc98_if_type == COM_IF_RSA98III) { rsa_buf_status = inb(com->rsabase + rsa_srr) & 0xc9; if ((rsa_buf_status & 0xc8) || !(rsa_buf_status & 0x01)) { siointr1(com); if (rsa_buf_status != (inb(com->rsabase + rsa_srr) & 0xc9)) possibly_more_intrs = TRUE; } } else #endif if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); mtx_unlock_spin(&sio_lock); #endif /* COM_MULTIPORT */ } static struct timespec siots[8]; static int siotso; static int volatile siotsunit = -1; static int sysctl_siots(SYSCTL_HANDLER_ARGS) { char buf[128]; long long delta; size_t len; int error, i, tso; for (i = 1, tso = siotso; i < tso; i++) { delta = (long long)(siots[i].tv_sec - siots[i - 1].tv_sec) * 1000000000 + (siots[i].tv_nsec - siots[i - 1].tv_nsec); len = sprintf(buf, "%lld\n", delta); if (delta >= 110000) len += sprintf(buf + len - 1, ": *** %ld.%09ld\n", (long)siots[i].tv_sec, siots[i].tv_nsec) - 1; if (i == tso - 1) buf[len - 1] = '\0'; error = SYSCTL_OUT(req, buf, len); if (error != 0) return (error); uio_yield(); } return (0); } SYSCTL_PROC(_machdep, OID_AUTO, siots, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_siots, "A", "sio timestamps"); static void siointr1(com) struct com_s *com; { u_char int_ctl; u_char int_ctl_new; u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; #ifdef PC98 u_char tmp = 0; u_char rsa_buf_status = 0; int rsa_tx_fifo_size = 0; #endif /* PC98 */ if (COM_IIR_TXRDYBUG(com->flags)) { int_ctl = inb(com->int_ctl_port); int_ctl_new = int_ctl; } else { int_ctl = 0; int_ctl_new = 0; } while (!com->gone) { #ifdef PC98 status_read:; if (IS_8251(com->pc98_if_type)) { if (com->pc98_8251fifo_enable) tmp = inb(I8251F_lsr); else tmp = inb(com->sts_port); more_intr: line_status = 0; if (com->pc98_8251fifo_enable) { if (tmp & STS8251F_TxRDY) line_status |= LSR_TXRDY; if (tmp & STS8251F_RxRDY) line_status |= LSR_RXRDY; if (tmp & STS8251F_TxEMP) line_status |= LSR_TSRE; if (tmp & STS8251F_PE) line_status |= LSR_PE; if (tmp & STS8251F_OE) line_status |= LSR_OE; if (tmp & STS8251F_BD_SD) line_status |= LSR_BI; } else { if (tmp & STS8251_TxRDY) line_status |= LSR_TXRDY; if (tmp & STS8251_RxRDY) line_status |= LSR_RXRDY; if (tmp & STS8251_TxEMP) line_status |= LSR_TSRE; if (tmp & STS8251_PE) line_status |= LSR_PE; if (tmp & STS8251_OE) line_status |= LSR_OE; if (tmp & STS8251_FE) line_status |= LSR_FE; if (tmp & STS8251_BD_SD) line_status |= LSR_BI; } } else { #endif /* PC98 */ if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) { modem_status = inb(com->modem_status_port); if ((modem_status ^ com->last_modem_status) & com->pps_bit) { pps_capture(&com->pps); pps_event(&com->pps, (modem_status & com->pps_bit) ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); } } line_status = inb(com->line_status_port); #ifdef PC98 } if (com->pc98_if_type == COM_IF_RSA98III) rsa_buf_status = inb(com->rsabase + rsa_srr); #endif /* PC98 */ /* input event? (check first to help avoid overruns) */ #ifndef PC98 while (line_status & LSR_RCV_MASK) { #else while ((line_status & LSR_RCV_MASK) || (com->pc98_if_type == COM_IF_RSA98III && (rsa_buf_status & 0x08))) { #endif /* PC98 */ /* break/unnattached error bits or real input? */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (com->pc98_8251fifo_enable) { recv_data = inb(I8251F_data); if (tmp & (STS8251F_PE | STS8251F_OE | STS8251F_BD_SD)) { pc98_i8251_or_cmd(com, CMD8251_ER); recv_data = 0; } } else { recv_data = inb(com->data_port); if (tmp & (STS8251_PE | STS8251_OE | STS8251_FE | STS8251_BD_SD)) { pc98_i8251_or_cmd(com, CMD8251_ER); recv_data = 0; } } } else if (com->pc98_if_type == COM_IF_RSA98III) { if (!(rsa_buf_status & 0x08)) recv_data = 0; else recv_data = inb(com->data_port); } else #endif if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); #ifdef DDB #ifdef ALT_BREAK_TO_DEBUGGER if (com->unit == comconsole && db_alt_break(recv_data, &com->alt_brk_state) != 0) breakpoint(); #endif /* ALT_BREAK_TO_DEBUGGER */ #endif /* DDB */ if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { /* * Don't store BI if IGNBRK or FE/PE if IGNPAR. * Otherwise, push the work to a higher level * (to handle PARMRK) if we're bypassing. * Otherwise, convert BI/FE and PE+INPCK to 0. * * This makes bypassing work right in the * usual "raw" case (IGNBRK set, and IGNPAR * and INPCK clear). * * Note: BI together with FE/PE means just BI. */ if (line_status & LSR_BI) { #if defined(DDB) && defined(BREAK_TO_DEBUGGER) if (com->unit == comconsole) { breakpoint(); goto cont; } #endif if (com->tp == NULL || com->tp->t_iflag & IGNBRK) goto cont; } else { if (com->tp == NULL || com->tp->t_iflag & IGNPAR) goto cont; } if (com->tp->t_state & TS_CAN_BYPASS_L_RINT && (line_status & (LSR_BI | LSR_FE) || com->tp->t_iflag & INPCK)) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) swi_sched(sio_fast_ih, 0); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; swi_sched(sio_slow_ih, SWI_DELAY); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) swi_sched(sio_fast_ih, 0); #endif ioptr[0] = recv_data; ioptr[com->ierroff] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) #ifdef PC98 IS_8251(com->pc98_if_type) ? com_tiocm_bic(com, TIOCM_RTS) : #endif outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) goto txrdy; /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) goto status_read; else #endif line_status = inb(com->line_status_port) & 0x7F; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) rsa_buf_status = inb(com->rsabase + rsa_srr); #endif /* PC98 */ } /* modem status change? (always check before doing output) */ #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { #endif modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; swi_sched(sio_fast_ih, 0); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } #ifdef PC98 } #endif txrdy: /* output queued and everything ready? */ #ifndef PC98 if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { #else if (((com->pc98_if_type == COM_IF_RSA98III) ? (rsa_buf_status & 0x02) : (line_status & LSR_TXRDY)) && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { #endif #ifdef PC98 Port_t tmp_data_port; if (IS_8251(com->pc98_if_type) && com->pc98_8251fifo_enable) tmp_data_port = I8251F_data; else tmp_data_port = com->data_port; #endif ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1 && com->unit != siotsunit) { u_int ocount; ocount = com->obufq.l_tail - ioptr; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) { rsa_buf_status = inb(com->rsabase + rsa_srr); rsa_tx_fifo_size = 1024; if (!(rsa_buf_status & 0x01)) rsa_tx_fifo_size = 2048; if (ocount > rsa_tx_fifo_size) ocount = rsa_tx_fifo_size; } else #endif if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do #ifdef PC98 outb(tmp_data_port, *ioptr++); #else outb(com->data_port, *ioptr++); #endif while (--ocount != 0); } else { #ifdef PC98 outb(tmp_data_port, *ioptr++); #else outb(com->data_port, *ioptr++); #endif ++com->bytes_out; if (com->unit == siotsunit && siotso < sizeof siots / sizeof siots[0]) nanouptime(&siots[siotso++]); } #ifdef PC98 if (IS_8251(com->pc98_if_type)) if (!(pc98_check_i8251_interrupt(com) & IEN_TxFLAG)) com_int_Tx_enable(com); #endif com->obufq.l_head = ioptr; if (COM_IIR_TXRDYBUG(com->flags)) int_ctl_new = int_ctl | IER_ETXRDY; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ if (COM_IIR_TXRDYBUG(com->flags)) int_ctl_new = int_ctl & ~IER_ETXRDY; com->state &= ~CS_BUSY; #if defined(PC98) if (IS_8251(com->pc98_if_type) && pc98_check_i8251_interrupt(com) & IEN_TxFLAG) com_int_Tx_disable(com); #endif } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; /* handle at high level ASAP */ swi_sched(sio_fast_ih, 0); } } #ifdef PC98 if (COM_IIR_TXRDYBUG(com->flags) && int_ctl != int_ctl_new) { if (com->pc98_if_type == COM_IF_RSA98III) { int_ctl_new &= ~(IER_ETXRDY | IER_ERXRDY); outb(com->int_ctl_port, int_ctl_new); outb(com->rsabase + rsa_ier, 0x1d); } else outb(com->int_ctl_port, int_ctl_new); } #else if (COM_IIR_TXRDYBUG(com->flags) && int_ctl != int_ctl_new) outb(com->int_ctl_port, int_ctl_new); #endif } #ifdef PC98 else if (line_status & LSR_TXRDY) { if (IS_8251(com->pc98_if_type)) if (pc98_check_i8251_interrupt(com) & IEN_TxFLAG) com_int_Tx_disable(com); } if (IS_8251(com->pc98_if_type)) { if (com->pc98_8251fifo_enable) { if ((tmp = inb(I8251F_lsr)) & STS8251F_RxRDY) goto more_intr; } else { if ((tmp = inb(com->sts_port)) & STS8251_RxRDY) goto more_intr; } } #endif /* finished? */ #ifndef COM_MULTIPORT #ifdef PC98 if (IS_8251(com->pc98_if_type)) return; #endif if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, td) dev_t dev; u_long cmd; caddr_t data; int flag; struct thread *td; { struct com_s *com; int error; int mynor; int s; struct tty *tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com == NULL || com->gone) return (ENODEV); if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(td); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } tp = com->tp; -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = ttyioctl(dev, cmd, data, flag, td); com->hotchar = ttyldoptim(tp); if (error != ENOTTY) return (error); s = spltty(); #ifdef PC98 if (IS_8251(com->pc98_if_type)) { switch (cmd) { case TIOCSBRK: com_send_break_on(com); break; case TIOCCBRK: com_send_break_off(com); break; case TIOCSDTR: com_tiocm_bis(com, TIOCM_DTR | TIOCM_RTS); break; case TIOCCDTR: com_tiocm_bic(com, TIOCM_DTR); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: com_tiocm_set(com, *(int *)data); break; case TIOCMBIS: com_tiocm_bis(com, *(int *)data); break; case TIOCMBIC: com_tiocm_bic(com, *(int *)data); break; case TIOCMGET: *(int *)data = com_tiocm_get(com); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(td); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); error = pps_ioctl(cmd, data, &com->pps); if (error == ENODEV) error = ENOTTY; return (error); } } else { #endif switch (cmd) { case TIOCSBRK: sio_setreg(com, com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(td); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); error = pps_ioctl(cmd, data, &com->pps); if (error == ENODEV) error = ENOTTY; return (error); } #ifdef PC98 } #endif splx(s); return (0); } /* software interrupt handler for SWI_TTY */ static void siopoll(void *dummy) { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < sio_numunits; ++unit) { struct com_s *com; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL || com->gone) { /* * Discard any events related to never-opened or * going-away devices. */ mtx_lock_spin(&sio_lock); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; mtx_unlock_spin(&sio_lock); continue; } if (com->iptr != com->ibuf) { mtx_lock_spin(&sio_lock); sioinput(com); mtx_unlock_spin(&sio_lock); } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { #endif mtx_lock_spin(&sio_lock); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; mtx_unlock_spin(&sio_lock); if (delta_modem_status & MSR_DCD) ttyld_modem(tp, com->prev_modem_status & MSR_DCD); #ifdef PC98 } #endif } if (com->state & CS_ODONE) { mtx_lock_spin(&sio_lock); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; mtx_unlock_spin(&sio_lock); if (!(com->state & CS_BUSY) && !(com->extra_state & CSE_BUSYCHECK)) { timeout(siobusycheck, com, hz / 100); com->extra_state |= CSE_BUSYCHECK; } ttyld_start(tp); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; u_int divisor; u_char dlbh; u_char dlbl; u_char efr_flowbits; int s; int unit; #ifdef PC98 u_char param = 0; #endif unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); if (com == NULL) return (ENODEV); #ifdef PC98 cfcr = 0; if (IS_8251(com->pc98_if_type)) { if (pc98_ttspeedtab(com, t->c_ospeed, &divisor) != 0) return (EINVAL); } else { #endif /* check requested parameters */ if (t->c_ispeed != (t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed)) return (EINVAL); divisor = siodivisor(com->rclk, t->c_ispeed); if (divisor == 0) return (EINVAL); #ifdef PC98 } #endif /* parameters are OK, convert them to the com struct and the device */ s = spltty(); #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (t->c_ospeed == 0) com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); else com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); } else #endif if (t->c_ospeed == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); cflag = t->c_cflag; #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { #endif switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. * * The fifo trigger level cannot be set at RX_HIGH for high * speed connections without further work on reducing * interrupt disablement times in other parts of the system, * without producing silo overflow errors. */ com->fifo_image = com->unit == siotsunit ? 0 : t->c_ispeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_MEDH; #ifdef COM_ESP /* * The Hayes ESP card needs the fifo DMA mode bit set * in compatibility mode. If not, it will interrupt * for each character received. */ if (com->esp) com->fifo_image |= FIFO_DMA_MODE; #endif sio_setreg(com, com_fifo, com->fifo_image); } #ifdef PC98 } #endif /* * This returns with interrupts disabled so that we can complete * the speed change atomically. Keeping interrupts disabled is * especially important while com_data is hidden. */ (void) siosetwater(com, t->c_ispeed); #ifdef PC98 if (IS_8251(com->pc98_if_type)) com_cflag_and_speed_set(com, cflag, t->c_ospeed); else { #endif sio_setreg(com, com_cfcr, cfcr | CFCR_DLAB); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (UMC8669F), setting them while input * is arriving loses sync until data stops arriving. */ dlbl = divisor & 0xFF; if (sio_getreg(com, com_dlbl) != dlbl) sio_setreg(com, com_dlbl, dlbl); dlbh = divisor >> 8; if (sio_getreg(com, com_dlbh) != dlbh) sio_setreg(com, com_dlbh, dlbh); #ifdef PC98 } #endif efr_flowbits = 0; if (cflag & CRTS_IFLOW) { com->state |= CS_RTS_IFLOW; efr_flowbits |= EFR_AUTORTS; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ #ifdef PC98 if (IS_8251(com->pc98_if_type)) com_tiocm_bis(com, TIOCM_RTS); else outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #endif } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) { param = inb(com->rsabase + rsa_msr); outb(com->rsabase + rsa_msr, param & 0x14); } #endif if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; efr_flowbits |= EFR_AUTOCTS; #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (!(pc98_get_modem_status(com) & TIOCM_CTS)) com->state &= ~CS_ODEVREADY; } else if (com->pc98_if_type == COM_IF_RSA98III) { /* Set automatic flow control mode */ outb(com->rsabase + rsa_msr, param | 0x08); } else #endif if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } #ifdef PC98 if (!IS_8251(com->pc98_if_type)) sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); #else if (com->st16650a) { sio_setreg(com, com_lcr, LCR_EFR_ENABLE); sio_setreg(com, com_efr, (sio_getreg(com, com_efr) & ~(EFR_AUTOCTS | EFR_AUTORTS)) | efr_flowbits); } sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); #endif /* XXX shouldn't call functions while intrs are disabled. */ com->hotchar = ttyldoptim(tp); mtx_unlock_spin(&sio_lock); splx(s); comstart(tp); if (com->ibufold != NULL) { free(com->ibufold, M_DEVBUF); com->ibufold = NULL; } return (0); } /* * This function must be called with the sio_lock mutex released and will * return with it obtained. */ static int siosetwater(com, speed) struct com_s *com; speed_t speed; { int cp4ticks; u_char *ibuf; int ibufsize; struct tty *tp; /* * Make the buffer size large enough to handle a softtty interrupt * latency of about 2 ticks without loss of throughput or data * (about 3 ticks if input flow control is not used or not honoured, * but a bit less for CS5-CS7 modes). */ cp4ticks = speed / 10 / hz * 4; for (ibufsize = 128; ibufsize < cp4ticks;) ibufsize <<= 1; #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) ibufsize = 2048; #endif if (ibufsize == com->ibufsize) { mtx_lock_spin(&sio_lock); return (0); } /* * Allocate input buffer. The extra factor of 2 in the size is * to allow for an error byte for each input byte. */ ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); if (ibuf == NULL) { mtx_lock_spin(&sio_lock); return (ENOMEM); } /* Initialize non-critical variables. */ com->ibufold = com->ibuf; com->ibufsize = ibufsize; tp = com->tp; if (tp != NULL) { tp->t_ififosize = 2 * ibufsize; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; } /* * Read current input buffer, if any. Continue with interrupts * disabled. */ mtx_lock_spin(&sio_lock); if (com->iptr != com->ibuf) sioinput(com); /*- * Initialize critical variables, including input buffer watermarks. * The external device is asked to stop sending when the buffer * exactly reaches high water, or when the high level requests it. * The high level is notified immediately (rather than at a later * clock tick) when this watermark is reached. * The buffer size is chosen so the watermark should almost never * be reached. * The low watermark is invisibly 0 since the buffer is always * emptied all at once. */ com->iptr = com->ibuf = ibuf; com->ibufend = ibuf + ibufsize; com->ierroff = ibufsize; com->ihighwater = ibuf + 3 * ibufsize / 4; return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); if (com == NULL) return; s = spltty(); mtx_lock_spin(&sio_lock); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if ((com_tiocm_get(com) & TIOCM_RTS) && (com->state & CS_RTS_IFLOW)) com_tiocm_bic(com, TIOCM_RTS); } else { if ((com->mcr_image & MCR_RTS) && (com->state & CS_RTS_IFLOW)) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } #else if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #endif } else { #ifdef PC98 if (IS_8251(com->pc98_if_type)) { if (!(com_tiocm_get(com) & TIOCM_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) com_tiocm_bis(com, TIOCM_RTS); } else { if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } #else if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #endif } mtx_unlock_spin(&sio_lock); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, #ifdef PC98 com->obufsize); #else sizeof com->obuf1); #endif com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; mtx_lock_spin(&sio_lock); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } mtx_unlock_spin(&sio_lock); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, #ifdef PC98 com->obufsize); #else sizeof com->obuf2); #endif com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; mtx_lock_spin(&sio_lock); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } mtx_unlock_spin(&sio_lock); } tp->t_state |= TS_BUSY; } mtx_lock_spin(&sio_lock); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ mtx_unlock_spin(&sio_lock); ttwwakeup(tp); splx(s); } static void comstop(tp, rw) struct tty *tp; int rw; { struct com_s *com; #ifdef PC98 int rsa98_tmp = 0; #endif com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com == NULL || com->gone) return; mtx_lock_spin(&sio_lock); if (rw & FWRITE) { #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { #endif if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif sio_setreg(com, com_fifo, FIFO_XMT_RST | com->fifo_image); #ifdef PC98 if (com->pc98_if_type == COM_IF_RSA98III) for (rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++) sio_setreg(com, com_fifo, FIFO_XMT_RST | com->fifo_image); } #endif com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { #ifdef PC98 if (!IS_8251(com->pc98_if_type)) { if (com->pc98_if_type == COM_IF_RSA98III) for (rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++) sio_getreg(com, com_data); #endif if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif sio_setreg(com, com_fifo, FIFO_RCV_RST | com->fifo_image); #ifdef PC98 } #endif com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } mtx_unlock_spin(&sio_lock); comstart(tp); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); mtx_lock_spin(&sio_lock); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } mtx_unlock_spin(&sio_lock); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < sio_numunits; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { mtx_lock_spin(&sio_lock); siointr1(com); mtx_unlock_spin(&sio_lock); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < sio_numunits; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; mtx_lock_spin(&sio_lock); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; mtx_unlock_spin(&sio_lock); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } #ifdef PC98 /* commint is called when modem control line changes */ static void commint(dev_t dev) { register struct tty *tp; int stat,delta; struct com_s *com; int mynor,unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); tp = com->tp; stat = com_tiocm_get(com); delta = com_tiocm_get_delta(com); if (com->state & CS_CTS_OFLOW) { if (stat & TIOCM_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } if ((delta & TIOCM_CAR) && (mynor & CALLOUT_MASK) == 0) { if (stat & TIOCM_CAR ) (void)ttyld_modem(tp, 1); else if (ttyld_modem(tp, 0) == 0) { /* negate DTR, RTS */ com_tiocm_bic(com, (tp->t_cflag & HUPCL) ? TIOCM_DTR|TIOCM_RTS|TIOCM_LE : TIOCM_LE ); /* disable IENABLE */ com_int_TxRx_disable( com ); } } } #endif /* * Following are all routines needed for SIO to act as console */ struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; /* * This is a function in order to not replicate "ttyd%d" more * places than absolutely necessary. */ static void siocnset(struct consdev *cd, int unit) { cd->cn_unit = unit; sprintf(cd->cn_name, "ttyd%d", unit); } #ifndef __alpha__ static speed_t siocngetspeed(Port_t, u_long rclk); #endif static void siocnclose(struct siocnstate *sp, Port_t iobase); static void siocnopen(struct siocnstate *sp, Port_t iobase, int speed); static void siocntxwait(Port_t iobase); #ifdef __alpha__ int siocnattach(int port, int speed); int siogdbattach(int port, int speed); int siogdbgetc(void); void siogdbputc(int c); #else static cn_probe_t siocnprobe; static cn_init_t siocninit; static cn_term_t siocnterm; #endif static cn_checkc_t siocncheckc; static cn_getc_t siocngetc; static cn_putc_t siocnputc; #ifndef __alpha__ CONS_DRIVER(sio, siocnprobe, siocninit, siocnterm, siocngetc, siocncheckc, siocnputc, NULL); #endif #if DDB > 0 static struct consdev gdbconsdev; #endif static void siocntxwait(iobase) Port_t iobase; { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } #ifndef __alpha__ /* * Read the serial port specified and try to figure out what speed * it's currently running at. We're assuming the serial port has * been initialized and is basicly idle. This routine is only intended * to be run at system startup. * * If the value read from the serial port doesn't make sense, return 0. */ static speed_t siocngetspeed(iobase, rclk) Port_t iobase; u_long rclk; { u_int divisor; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(iobase + com_dlbl); dlbh = inb(iobase + com_dlbh); outb(iobase + com_cfcr, cfcr); divisor = dlbh << 8 | dlbl; /* XXX there should be more sanity checking. */ if (divisor == 0) return (CONSPEED); return (rclk / (16UL * divisor)); } #endif static void siocnopen(sp, iobase, speed) struct siocnstate *sp; Port_t iobase; int speed; { u_int divisor; u_char dlbh; u_char dlbl; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(iobase); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (Startech), setting them clears the * data input register. This also reduces the effects of the * UMC8669F bug. */ divisor = siodivisor(comdefaultrclk, speed); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp, iobase) struct siocnstate *sp; Port_t iobase; { /* * Restore the device control registers. */ siocntxwait(iobase); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); if (sp->dlbl != inb(iobase + com_dlbl)) outb(iobase + com_dlbl, sp->dlbl); if (sp->dlbh != inb(iobase + com_dlbh)) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } #ifndef __alpha__ static void siocnprobe(cp) struct consdev *cp; { speed_t boot_speed; u_char cfcr; u_int divisor; int s, unit; struct siocnstate sp; /* * Find our first enabled console, if any. If it is a high-level * console device, then initialize it and return successfully. * If it is a low-level console device, then initialize it and * return unsuccessfully. It must be initialized in both cases * for early use by console drivers and debuggers. Initializing * the hardware is not necessary in all cases, since the i/o * routines initialize it on the fly, but it is necessary if * input might arrive while the hardware is switched back to an * uninitialized state. We can't handle multiple console devices * yet because our low-level routines don't take a device arg. * We trust the user to set the console flags properly so that we * don't need to probe. */ cp->cn_pri = CN_DEAD; for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ int flags; if (resource_disabled("sio", unit)) continue; if (resource_int_value("sio", unit, "flags", &flags)) continue; if (COM_CONSOLE(flags) || COM_DEBUGGER(flags)) { int port; Port_t iobase; if (resource_int_value("sio", unit, "port", &port)) continue; iobase = port; s = spltty(); if (boothowto & RB_SERIAL) { boot_speed = siocngetspeed(iobase, comdefaultrclk); if (boot_speed) comdefaultrate = boot_speed; } /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, comdefaultrate); outb(iobase + com_dlbl, divisor & 0xff); outb(iobase + com_dlbh, divisor >> 8); outb(iobase + com_cfcr, cfcr); siocnopen(&sp, iobase, comdefaultrate); splx(s); if (COM_CONSOLE(flags) && !COM_LLCONSOLE(flags)) { siocnset(cp, unit); cp->cn_pri = COM_FORCECONSOLE(flags) || boothowto & RB_SERIAL ? CN_REMOTE : CN_NORMAL; siocniobase = iobase; siocnunit = unit; } if (COM_DEBUGGER(flags)) { printf("sio%d: gdb debugging port\n", unit); siogdbiobase = iobase; siogdbunit = unit; #if DDB > 0 siocnset(&gdbconsdev, unit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; #endif } } } #ifdef __i386__ #if DDB > 0 /* * XXX Ugly Compatability. * If no gdb port has been specified, set it to be the console * as some configuration files don't specify the gdb port. */ if (gdb_arg == NULL && (boothowto & RB_GDB)) { printf("Warning: no GDB port specified. Defaulting to sio%d.\n", siocnunit); printf("Set flag 0x80 on desired GDB port in your\n"); printf("configuration file (currently sio only).\n"); siogdbiobase = siocniobase; siogdbunit = siocnunit; siocnset(&gdbconsdev, siocnunit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; } #endif #endif } static void siocninit(cp) struct consdev *cp; { comconsole = cp->cn_unit; } static void siocnterm(cp) struct consdev *cp; { comconsole = -1; } #endif #ifdef __alpha__ CONS_DRIVER(sio, NULL, NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL); int siocnattach(port, speed) int port; int speed; { int s; u_char cfcr; u_int divisor; struct siocnstate sp; int unit = 0; /* XXX random value! */ siocniobase = port; siocnunit = unit; comdefaultrate = speed; sio_consdev.cn_pri = CN_NORMAL; siocnset(&sio_consdev, unit); s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, comdefaultrate); outb(siocniobase + com_dlbl, divisor & 0xff); outb(siocniobase + com_dlbh, divisor >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp, siocniobase, comdefaultrate); splx(s); cnadd(&sio_consdev); return (0); } int siogdbattach(port, speed) int port; int speed; { int s; u_char cfcr; u_int divisor; struct siocnstate sp; int unit = 1; /* XXX random value! */ siogdbiobase = port; gdbdefaultrate = speed; printf("sio%d: gdb debugging port\n", unit); siogdbunit = unit; #if DDB > 0 siocnset(&gdbconsdev, unit); gdb_arg = &gdbconsdev; gdb_getc = siocngetc; gdb_putc = siocnputc; #endif s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siogdbiobase + com_cfcr); outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); divisor = siodivisor(comdefaultrclk, gdbdefaultrate); outb(siogdbiobase + com_dlbl, divisor & 0xff); outb(siogdbiobase + com_dlbh, divisor >> 8); outb(siogdbiobase + com_cfcr, cfcr); siocnopen(&sp, siogdbiobase, gdbdefaultrate); splx(s); return (0); } #endif static int siocncheckc(struct consdev *cd) { int c; Port_t iobase; int s; struct siocnstate sp; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = -1; siocnclose(&sp, iobase); splx(s); return (c); } static int siocngetc(struct consdev *cd) { int c; Port_t iobase; int s; struct siocnstate sp; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } static void siocnputc(struct consdev *cd, int c) { int need_unlock; int s; struct siocnstate sp; Port_t iobase; speed_t speed; if (cd->cn_unit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); need_unlock = 0; if (sio_inited == 2 && !mtx_owned(&sio_lock)) { mtx_lock_spin(&sio_lock); need_unlock = 1; } siocnopen(&sp, iobase, speed); siocntxwait(iobase); outb(iobase + com_data, c); siocnclose(&sp, iobase); if (need_unlock) mtx_unlock_spin(&sio_lock); splx(s); } #ifdef __alpha__ int siogdbgetc() { int c; Port_t iobase; speed_t speed; int s; struct siocnstate sp; if (siogdbunit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } void siogdbputc(c) int c; { Port_t iobase; speed_t speed; int s; struct siocnstate sp; if (siogdbunit == siocnunit) { iobase = siocniobase; speed = comdefaultrate; } else { iobase = siogdbiobase; speed = gdbdefaultrate; } s = spltty(); siocnopen(&sp, iobase, speed); siocntxwait(siogdbiobase); outb(siogdbiobase + com_data, c); siocnclose(&sp, siogdbiobase); splx(s); } #endif #ifdef PC98 /* * pc98 local function */ static void com_tiocm_set(struct com_s *com, int msr) { int s; int tmp = 0; int mask = CMD8251_TxEN|CMD8251_RxEN|CMD8251_DTR|CMD8251_RTS; s=spltty(); com->pc98_prev_modem_status = ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ) | ( com->pc98_prev_modem_status & ~(TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); tmp |= (CMD8251_TxEN|CMD8251_RxEN); if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_clear_or_cmd( com, mask, tmp ); splx(s); } static void com_tiocm_bis(struct com_s *com, int msr) { int s; int tmp = 0; s=spltty(); com->pc98_prev_modem_status |= ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); tmp |= CMD8251_TxEN|CMD8251_RxEN; if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_or_cmd( com, tmp ); splx(s); } static void com_tiocm_bic(struct com_s *com, int msr) { int s; int tmp = msr; s=spltty(); com->pc98_prev_modem_status &= ~( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_clear_cmd( com, tmp ); splx(s); } static int com_tiocm_get(struct com_s *com) { return( com->pc98_prev_modem_status ); } static int com_tiocm_get_delta(struct com_s *com) { int tmp; tmp = com->pc98_modem_delta; com->pc98_modem_delta = 0; return( tmp ); } /* convert to TIOCM_?? ( ioctl.h ) */ static int pc98_get_modem_status(struct com_s *com) { register int msr; msr = com->pc98_prev_modem_status & ~(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); if (com->pc98_8251fifo_enable) { int stat2; stat2 = inb(I8251F_msr); if ( stat2 & CICSCDF_CD ) msr |= TIOCM_CAR; if ( stat2 & CICSCDF_CI ) msr |= TIOCM_RI; if ( stat2 & CICSCDF_DR ) msr |= TIOCM_DSR; if ( stat2 & CICSCDF_CS ) msr |= TIOCM_CTS; #if COM_CARRIER_DETECT_EMULATE if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { msr |= TIOCM_CAR; } #endif } else { int stat, stat2; stat = inb(com->sts_port); stat2 = inb(com->in_modem_port); if ( !(stat2 & CICSCD_CD) ) msr |= TIOCM_CAR; if ( !(stat2 & CICSCD_CI) ) msr |= TIOCM_RI; if ( stat & STS8251_DSR ) msr |= TIOCM_DSR; if ( !(stat2 & CICSCD_CS) ) msr |= TIOCM_CTS; #if COM_CARRIER_DETECT_EMULATE if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { msr |= TIOCM_CAR; } #endif } return(msr); } static void pc98_check_msr(void* chan) { int msr, delta; int s; register struct tty *tp; struct com_s *com; int mynor; int unit; dev_t dev; dev=(dev_t)chan; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); tp = com->tp; s = spltty(); msr = pc98_get_modem_status(com); /* make change flag */ delta = msr ^ com->pc98_prev_modem_status; if ( delta & TIOCM_CAR ) { if ( com->modem_car_chg_timer ) { if ( -- com->modem_car_chg_timer ) msr ^= TIOCM_CAR; } else { if ((com->modem_car_chg_timer = (msr & TIOCM_CAR) ? DCD_ON_RECOGNITION : DCD_OFF_TOLERANCE) != 0) msr ^= TIOCM_CAR; } } else com->modem_car_chg_timer = 0; delta = ( msr ^ com->pc98_prev_modem_status ) & (TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); com->pc98_prev_modem_status = msr; delta = ( com->pc98_modem_delta |= delta ); splx(s); if ( com->modem_checking || (tp->t_state & (TS_ISOPEN)) ) { if ( delta ) { commint(dev); } timeout(pc98_check_msr, (caddr_t)dev, PC98_CHECK_MODEM_INTERVAL); } else { com->modem_checking = 0; } } static void pc98_msrint_start(dev_t dev) { struct com_s *com; int mynor; int unit; int s = spltty(); mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); /* modem control line check routine envoke interval is 1/10 sec */ if ( com->modem_checking == 0 ) { com->pc98_prev_modem_status = pc98_get_modem_status(com); com->pc98_modem_delta = 0; timeout(pc98_check_msr, (caddr_t)dev, PC98_CHECK_MODEM_INTERVAL); com->modem_checking = 1; } splx(s); } static void pc98_disable_i8251_interrupt(struct com_s *com, int mod) { /* disable interrupt */ register int tmp; mod |= ~(IEN_Tx|IEN_TxEMP|IEN_Rx); COM_INT_DISABLE tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); outb( com->intr_ctrl_port, (com->intr_enable&=~mod) | tmp ); COM_INT_ENABLE } static void pc98_enable_i8251_interrupt(struct com_s *com, int mod) { register int tmp; COM_INT_DISABLE tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); outb( com->intr_ctrl_port, (com->intr_enable|=mod) | tmp ); COM_INT_ENABLE } static int pc98_check_i8251_interrupt(struct com_s *com) { return ( com->intr_enable & 0x07 ); } static void pc98_i8251_clear_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE tmp = com->pc98_prev_siocmd & ~(x); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE); COM_INT_ENABLE } static void pc98_i8251_or_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); tmp = com->pc98_prev_siocmd | (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE); COM_INT_ENABLE } static void pc98_i8251_set_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); tmp = (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE); COM_INT_ENABLE } static void pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x) { int tmp; COM_INT_DISABLE if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); tmp = com->pc98_prev_siocmd & ~(clr); tmp |= (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE); COM_INT_ENABLE } static int pc98_i8251_get_cmd(struct com_s *com) { return com->pc98_prev_siocmd; } static int pc98_i8251_get_mod(struct com_s *com) { return com->pc98_prev_siomod; } static void pc98_i8251_reset(struct com_s *com, int mode, int command) { if (com->pc98_8251fifo_enable) outb(I8251F_fcr, 0); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, CMD8251_RESET); /* internal reset */ DELAY(2); outb(com->cmd_port, mode ); /* mode register */ com->pc98_prev_siomod = mode; DELAY(2); pc98_i8251_set_cmd( com, (command|CMD8251_ER) ); DELAY(10); if (com->pc98_8251fifo_enable) outb(I8251F_fcr, CTRL8251F_ENABLE | CTRL8251F_XMT_RST | CTRL8251F_RCV_RST); } static void pc98_check_sysclock(void) { /* get system clock from port */ if ( pc98_machine_type & M_8M ) { /* 8 MHz system & H98 */ sysclock = 8; } else { /* 5 MHz system */ sysclock = 5; } } static void com_cflag_and_speed_set( struct com_s *com, int cflag, int speed) { int cfcr=0; int previnterrupt; u_int count; if (pc98_ttspeedtab(com, speed, &count) != 0) return; previnterrupt = pc98_check_i8251_interrupt(com); pc98_disable_i8251_interrupt( com, IEN_Tx|IEN_TxEMP|IEN_Rx ); switch ( cflag&CSIZE ) { case CS5: cfcr = MOD8251_5BITS; break; case CS6: cfcr = MOD8251_6BITS; break; case CS7: cfcr = MOD8251_7BITS; break; case CS8: cfcr = MOD8251_8BITS; break; } if ( cflag&PARENB ) { if ( cflag&PARODD ) cfcr |= MOD8251_PODD; else cfcr |= MOD8251_PEVEN; } else cfcr |= MOD8251_PDISAB; if ( cflag&CSTOPB ) cfcr |= MOD8251_STOP2; else cfcr |= MOD8251_STOP1; if ( count & 0x10000 ) cfcr |= MOD8251_CLKX1; else cfcr |= MOD8251_CLKX16; if (epson_machine_id != 0x20) { /* XXX */ int tmp; while (!((tmp = inb(com->sts_port)) & STS8251_TxEMP)) ; } /* set baud rate from ospeed */ pc98_set_baud_rate( com, count ); if ( cfcr != pc98_i8251_get_mod(com) ) pc98_i8251_reset(com, cfcr, pc98_i8251_get_cmd(com) ); pc98_enable_i8251_interrupt( com, previnterrupt ); } static int pc98_ttspeedtab(struct com_s *com, int speed, u_int *divisor) { int if_type, effect_sp, count = -1, mod; if_type = com->pc98_if_type & 0x0f; switch (com->pc98_if_type) { case COM_IF_INTERNAL: if (PC98SIO_baud_rate_port(if_type) != -1) { count = ttspeedtab(speed, if_8251_type[if_type].speedtab); if (count > 0) { count |= COM1_EXT_CLOCK; break; } } /* for *1CLK asynchronous! mode, TEFUTEFU */ mod = (sysclock == 5) ? 2457600 : 1996800; effect_sp = ttspeedtab( speed, pc98speedtab ); if ( effect_sp < 0 ) /* XXX */ effect_sp = ttspeedtab( (speed - 1), pc98speedtab ); if ( effect_sp <= 0 ) return effect_sp; if ( effect_sp == speed ) mod /= 16; if ( mod % effect_sp ) return(-1); count = mod / effect_sp; if ( count > 65535 ) return(-1); if ( effect_sp != speed ) count |= 0x10000; break; case COM_IF_PC9861K_1: case COM_IF_PC9861K_2: count = 1; break; case COM_IF_IND_SS_1: case COM_IF_IND_SS_2: case COM_IF_PIO9032B_1: case COM_IF_PIO9032B_2: count = ttspeedtab( speed, if_8251_type[if_type].speedtab ); break; case COM_IF_B98_01_1: case COM_IF_B98_01_2: count = ttspeedtab( speed, if_8251_type[if_type].speedtab ); #ifdef B98_01_OLD if (count == 0 || count == 1) { count += 4; count |= 0x20000; /* x1 mode for 76800 and 153600 */ } #endif break; } if (count < 0) return count; *divisor = (u_int) count; return 0; } static void pc98_set_baud_rate( struct com_s *com, u_int count ) { int if_type, io, s; if_type = com->pc98_if_type & 0x0f; io = rman_get_start(com->ioportres) & 0xff00; switch (com->pc98_if_type) { case COM_IF_INTERNAL: if (PC98SIO_baud_rate_port(if_type) != -1) { if (count & COM1_EXT_CLOCK) { outb((Port_t)PC98SIO_baud_rate_port(if_type), count & 0xff); break; } else { outb((Port_t)PC98SIO_baud_rate_port(if_type), 0x09); } } if (count == 0) return; /* set i8253 */ s = splclock(); if (count != 3) outb( 0x77, 0xb6 ); else outb( 0x77, 0xb4 ); outb( 0x5f, 0); outb( 0x75, count & 0xff ); outb( 0x5f, 0); outb( 0x75, (count >> 8) & 0xff ); splx(s); break; case COM_IF_IND_SS_1: case COM_IF_IND_SS_2: outb(io | PC98SIO_intr_ctrl_port(if_type), 0); outb(io | PC98SIO_baud_rate_port(if_type), 0); outb(io | PC98SIO_baud_rate_port(if_type), 0xc0); outb(io | PC98SIO_baud_rate_port(if_type), (count >> 8) | 0x80); outb(io | PC98SIO_baud_rate_port(if_type), count & 0xff); break; case COM_IF_PIO9032B_1: case COM_IF_PIO9032B_2: outb(io | PC98SIO_baud_rate_port(if_type), count); break; case COM_IF_B98_01_1: case COM_IF_B98_01_2: outb(io | PC98SIO_baud_rate_port(if_type), count & 0x0f); #ifdef B98_01_OLD /* * Some old B98_01 board should be controlled * in different way, but this hasn't been tested yet. */ outb(io | PC98SIO_func_port(if_type), (count & 0x20000) ? 0xf0 : 0xf2); #endif break; } } static int pc98_check_if_type(device_t dev, struct siodev *iod) { int irr, io, if_type, tmp; static short irq_tab[2][8] = { { 3, 5, 6, 9, 10, 12, 13, -1}, { 3, 10, 12, 13, 5, 6, 9, -1} }; if_type = iod->if_type & 0x0f; iod->irq = 0; io = isa_get_port(dev) & 0xff00; if (IS_8251(iod->if_type)) { if (PC98SIO_func_port(if_type) != -1) { outb(io | PC98SIO_func_port(if_type), 0xf2); tmp = ttspeedtab(9600, if_8251_type[if_type].speedtab); if (tmp != -1 && PC98SIO_baud_rate_port(if_type) != -1) outb(io | PC98SIO_baud_rate_port(if_type), tmp); } iod->cmd = io | PC98SIO_cmd_port(if_type); iod->sts = io | PC98SIO_sts_port(if_type); iod->mod = io | PC98SIO_in_modem_port(if_type); iod->ctrl = io | PC98SIO_intr_ctrl_port(if_type); if (iod->if_type == COM_IF_INTERNAL) { iod->irq = 4; if (pc98_check_8251vfast()) { PC98SIO_baud_rate_port(if_type) = I8251F_div; if_8251_type[if_type].speedtab = pc98fast_speedtab; } } else { tmp = inb( iod->mod ) & if_8251_type[if_type].irr_mask; if ((isa_get_port(dev) & 0xff) == IO_COM2) iod->irq = irq_tab[0][tmp]; else iod->irq = irq_tab[1][tmp]; } } else { irr = if_16550a_type[if_type].irr_read; #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(device_get_flags(dev)) || device_get_unit(dev) == COM_MPMASTER(device_get_flags(dev))) #endif if (irr != -1) { tmp = inb(io | irr); if (isa_get_port(dev) & 0x01) /* XXX depend on RSB-384 */ iod->irq = irq_tab[1][tmp >> 3]; else iod->irq = irq_tab[0][tmp & 0x07]; } } if ( iod->irq == -1 ) return -1; return 0; } static void pc98_set_ioport(struct com_s *com) { int if_type = com->pc98_if_type & 0x0f; Port_t io = rman_get_start(com->ioportres) & 0xff00; pc98_check_sysclock(); com->data_port = io | PC98SIO_data_port(if_type); com->cmd_port = io | PC98SIO_cmd_port(if_type); com->sts_port = io | PC98SIO_sts_port(if_type); com->in_modem_port = io | PC98SIO_in_modem_port(if_type); com->intr_ctrl_port = io | PC98SIO_intr_ctrl_port(if_type); } static int pc98_check_8251vfast(void) { int i; outb(I8251F_div, 0x8c); DELAY(10); for (i = 0; i < 100; i++) { if ((inb(I8251F_div) & 0x80) != 0) { i = 0; break; } DELAY(1); } outb(I8251F_div, 0); DELAY(10); for (; i < 100; i++) { if ((inb(I8251F_div) & 0x80) == 0) return 1; DELAY(1); } return 0; } static int pc98_check_8251fifo(void) { u_char tmp1, tmp2; tmp1 = inb(I8251F_iir); DELAY(10); tmp2 = inb(I8251F_iir); if (((tmp1 ^ tmp2) & 0x40) != 0 && ((tmp1 | tmp2) & 0x20) == 0) return 1; return 0; } #endif /* PC98 defined */ Index: head/sys/sys/ioctl.h =================================================================== --- head/sys/sys/ioctl.h (revision 130343) +++ head/sys/sys/ioctl.h (revision 130344) @@ -1,79 +1,79 @@ /*- * Copyright (c) 1982, 1986, 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 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. * * @(#)ioctl.h 8.6 (Berkeley) 3/28/94 * $FreeBSD$ */ #ifndef _SYS_IOCTL_H_ #define _SYS_IOCTL_H_ #ifdef _KERNEL #if defined(__GNUC__) || defined(__INTEL_COMPILER) #warning "Don't #include ioctl.h in the kernel. Include xxxio.h instead." #endif #endif #include /* * Pun for SunOS prior to 3.2. SunOS 3.2 and later support TIOCGWINSZ * and TIOCSWINSZ (yes, even 3.2-3.5, the fact that it wasn't documented * notwithstanding). */ struct ttysize { unsigned short ts_lines; unsigned short ts_cols; unsigned short ts_xxx; unsigned short ts_yyy; }; #define TIOCGSIZE TIOCGWINSZ #define TIOCSSIZE TIOCSWINSZ #include #include #include #endif /* !_SYS_IOCTL_H_ */ /* * Keep outside _SYS_IOCTL_H_ * Compatibility with old terminal driver * * Source level -> #define USE_OLD_TTY - * Kernel level -> options COMPAT_43 or COMPAT_SUNOS + * Kernel level -> options COMPAT_43 */ -#if defined(USE_OLD_TTY) || defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(USE_OLD_TTY) || defined(COMPAT_43) #include #endif Index: head/sys/sys/signal.h =================================================================== --- head/sys/sys/signal.h (revision 130343) +++ head/sys/sys/signal.h (revision 130344) @@ -1,354 +1,351 @@ /*- * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 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. * * @(#)signal.h 8.4 (Berkeley) 5/4/95 * $FreeBSD$ */ #ifndef _SYS_SIGNAL_H_ #define _SYS_SIGNAL_H_ #include #include #include #include /* sig_atomic_t; trap codes; sigcontext */ /* * System defined signals. */ #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGHUP 1 /* hangup */ #endif #define SIGINT 2 /* interrupt */ #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGQUIT 3 /* quit */ #endif #define SIGILL 4 /* illegal instr. (not reset when caught) */ #if __XSI_VISIBLE #define SIGTRAP 5 /* trace trap (not reset when caught) */ #endif #define SIGABRT 6 /* abort() */ #if __BSD_VISIBLE #define SIGIOT SIGABRT /* compatibility */ #define SIGEMT 7 /* EMT instruction */ #endif #define SIGFPE 8 /* floating point exception */ #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGKILL 9 /* kill (cannot be caught or ignored) */ #endif #if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE #define SIGBUS 10 /* bus error */ #endif #define SIGSEGV 11 /* segmentation violation */ #if __BSD_VISIBLE #define SIGSYS 12 /* non-existent system call invoked */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGPIPE 13 /* write on a pipe with no one to read it */ #define SIGALRM 14 /* alarm clock */ #endif #define SIGTERM 15 /* software termination signal from kill */ #if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE #define SIGURG 16 /* urgent condition on IO channel */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGSTOP 17 /* sendable stop signal not from tty */ #define SIGTSTP 18 /* stop signal from tty */ #define SIGCONT 19 /* continue a stopped process */ #define SIGCHLD 20 /* to parent on child stop or exit */ #define SIGTTIN 21 /* to readers pgrp upon background tty read */ #define SIGTTOU 22 /* like TTIN if (tp->t_local<OSTOP) */ #endif #if __BSD_VISIBLE #define SIGIO 23 /* input/output possible signal */ #endif #if __XSI_VISIBLE #define SIGXCPU 24 /* exceeded CPU time limit */ #define SIGXFSZ 25 /* exceeded file size limit */ #define SIGVTALRM 26 /* virtual time alarm */ #define SIGPROF 27 /* profiling time alarm */ #endif #if __BSD_VISIBLE #define SIGWINCH 28 /* window size changes */ #define SIGINFO 29 /* information request */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGUSR1 30 /* user defined signal 1 */ #define SIGUSR2 31 /* user defined signal 2 */ #endif #if __BSD_VISIBLE #define SIGTHR 32 /* Thread interrupt. */ #endif /* * XXX missing SIGRTMIN, SIGRTMAX. */ #define SIG_DFL ((__sighandler_t *)0) #define SIG_IGN ((__sighandler_t *)1) #define SIG_ERR ((__sighandler_t *)-1) /* * XXX missing SIG_HOLD. */ /*- * Type of a signal handling function. * * Language spec sez signal handlers take exactly one arg, even though we * actually supply three. Ugh! * * We don't try to hide the difference by leaving out the args because * that would cause warnings about conformant programs. Nonconformant * programs can avoid the warnings by casting to (__sighandler_t *) or * sig_t before calling signal() or assigning to sa_handler or sv_handler. * * The kernel should reverse the cast before calling the function. It * has no way to do this, but on most machines 1-arg and 3-arg functions * have the same calling protocol so there is no problem in practice. * A bit in sa_flags could be used to specify the number of args. */ typedef void __sighandler_t(int); #if __POSIX_VISIBLE || __XSI_VISIBLE #ifndef _SIGSET_T_DECLARED #define _SIGSET_T_DECLARED typedef __sigset_t sigset_t; #endif #endif #if __POSIX_VISIBLE >= 199309 || __XSI_VISIBLE >= 500 union sigval { /* Members as suggested by Annex C of POSIX 1003.1b. */ int sigval_int; void *sigval_ptr; }; #endif #if __POSIX_VISIBLE >= 199309 struct sigevent { int sigev_notify; /* Notification type */ union { int __sigev_signo; /* Signal number */ int __sigev_notify_kqueue; } __sigev_u; union sigval sigev_value; /* Signal value */ /* * XXX missing sigev_notify_function, sigev_notify_attributes. */ }; #define sigev_signo __sigev_u.__sigev_signo #if __BSD_VISIBLE #define sigev_notify_kqueue __sigev_u.__sigev_notify_kqueue #endif #define SIGEV_NONE 0 /* No async notification */ #define SIGEV_SIGNAL 1 /* Generate a queued signal */ #if __BSD_VISIBLE #define SIGEV_KEVENT 3 /* Generate a kevent */ #endif /* * XXX missing SIGEV_THREAD. */ #endif /* __POSIX_VISIBLE >= 199309 */ #if __POSIX_VISIBLE >= 199309 || __XSI_VISIBLE typedef struct __siginfo { int si_signo; /* signal number */ int si_errno; /* errno association */ /* * Cause of signal, one of the SI_ macros or signal-specific * values, i.e. one of the FPE_... values for SIGFPE. This * value is equivalent to the second argument to an old-style * FreeBSD signal handler. */ int si_code; /* signal code */ __pid_t si_pid; /* sending process */ __uid_t si_uid; /* sender's ruid */ int si_status; /* exit value */ void *si_addr; /* faulting instruction */ union sigval si_value; /* signal value */ long si_band; /* band event for SIGPOLL */ int __spare__[7]; /* gimme some slack */ } siginfo_t; #endif #if __POSIX_VISIBLE || __XSI_VISIBLE struct __siginfo; /* * Signal vector "template" used in sigaction call. */ struct sigaction { union { void (*__sa_handler)(int); void (*__sa_sigaction)(int, struct __siginfo *, void *); } __sigaction_u; /* signal handler */ int sa_flags; /* see signal options below */ sigset_t sa_mask; /* signal mask to apply */ }; #define sa_handler __sigaction_u.__sa_handler #endif #if __XSI_VISIBLE /* If SA_SIGINFO is set, sa_sigaction must be used instead of sa_handler. */ #define sa_sigaction __sigaction_u.__sa_sigaction #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SA_NOCLDSTOP 0x0008 /* do not generate SIGCHLD on child stop */ #endif /* __POSIX_VISIBLE || __XSI_VISIBLE */ #if __XSI_VISIBLE #define SA_ONSTACK 0x0001 /* take signal on signal stack */ #define SA_RESTART 0x0002 /* restart system call on signal return */ #define SA_RESETHAND 0x0004 /* reset to SIG_DFL when taking signal */ #define SA_NODEFER 0x0010 /* don't mask the signal we're delivering */ #define SA_NOCLDWAIT 0x0020 /* don't keep zombies around */ #define SA_SIGINFO 0x0040 /* signal handler with SA_SIGINFO args */ #endif #if __BSD_VISIBLE /* XXX dubious. */ -#ifdef COMPAT_SUNOS -#define SA_USERTRAMP 0x0100 /* do not bounce off kernel's sigtramp */ -#endif #endif #if __BSD_VISIBLE #define NSIG 32 /* number of old signals (counting 0) */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SI_USER 0x10001 #define SI_QUEUE 0x10002 #define SI_TIMER 0x10003 #define SI_ASYNCIO 0x10004 #define SI_MESGQ 0x10005 #endif #if __BSD_VISIBLE #define SI_UNDEFINED 0 #endif #if __BSD_VISIBLE typedef __sighandler_t *sig_t; /* type of pointer to a signal function */ typedef void __siginfohandler_t(int, struct __siginfo *, void *); #endif #if __XSI_VISIBLE /* * Structure used in sigaltstack call. */ #if __BSD_VISIBLE typedef struct sigaltstack { #else typedef struct { #endif char *ss_sp; /* signal stack base */ __size_t ss_size; /* signal stack length */ int ss_flags; /* SS_DISABLE and/or SS_ONSTACK */ } stack_t; #define SS_ONSTACK 0x0001 /* take signal on alternate stack */ #define SS_DISABLE 0x0004 /* disable taking signals on alternate stack */ #define SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended stack size */ #endif #if __BSD_VISIBLE /* * 4.3 compatibility: * Signal vector "template" used in sigvec call. */ struct sigvec { __sighandler_t *sv_handler; /* signal handler */ int sv_mask; /* signal mask to apply */ int sv_flags; /* see signal options below */ }; #define SV_ONSTACK SA_ONSTACK #define SV_INTERRUPT SA_RESTART /* same bit, opposite sense */ #define SV_RESETHAND SA_RESETHAND #define SV_NODEFER SA_NODEFER #define SV_NOCLDSTOP SA_NOCLDSTOP #define SV_SIGINFO SA_SIGINFO #define sv_onstack sv_flags /* isn't compatibility wonderful! */ #endif /* Keep this in one place only */ #if defined(_KERNEL) && defined(COMPAT_43) && \ !defined(__i386__) && !defined(__alpha__) struct osigcontext { int _not_used; }; #endif #if __XSI_VISIBLE /* * Structure used in sigstack call. */ struct sigstack { /* XXX ss_sp's type should be `void *'. */ char *ss_sp; /* signal stack pointer */ int ss_onstack; /* current status */ }; #endif #if __BSD_VISIBLE || __POSIX_VISIBLE > 0 && __POSIX_VISIBLE <= 200112 /* * Macro for converting signal number to a mask suitable for * sigblock(). */ #define sigmask(m) (1 << ((m)-1)) #endif #if __BSD_VISIBLE #define BADSIG SIG_ERR #endif #if __POSIX_VISIBLE || __XSI_VISIBLE /* * Flags for sigprocmask: */ #define SIG_BLOCK 1 /* block specified signal set */ #define SIG_UNBLOCK 2 /* unblock specified signal set */ #define SIG_SETMASK 3 /* set specified signal set */ #endif /* * For historical reasons; programs expect signal's return value to be * defined by . */ __BEGIN_DECLS __sighandler_t *signal(int, __sighandler_t *); __END_DECLS #endif /* !_SYS_SIGNAL_H_ */ Index: head/sys/vm/vm_mmap.c =================================================================== --- head/sys/vm/vm_mmap.c (revision 130343) +++ head/sys/vm/vm_mmap.c (revision 130344) @@ -1,1319 +1,1319 @@ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. * * from: Utah $Hdr: vm_mmap.c 1.6 91/10/21$ * * @(#)vm_mmap.c 8.4 (Berkeley) 1/12/94 */ /* * Mapped file (mmap) interface to VM */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _SYS_SYSPROTO_H_ struct sbrk_args { int incr; }; #endif static int max_proc_mmap; SYSCTL_INT(_vm, OID_AUTO, max_proc_mmap, CTLFLAG_RW, &max_proc_mmap, 0, ""); /* * Set the maximum number of vm_map_entry structures per process. Roughly * speaking vm_map_entry structures are tiny, so allowing them to eat 1/100 * of our KVM malloc space still results in generous limits. We want a * default that is good enough to prevent the kernel running out of resources * if attacked from compromised user account but generous enough such that * multi-threaded processes are not unduly inconvenienced. */ static void vmmapentry_rsrc_init(void *); SYSINIT(vmmersrc, SI_SUB_KVM_RSRC, SI_ORDER_FIRST, vmmapentry_rsrc_init, NULL) static void vmmapentry_rsrc_init(dummy) void *dummy; { max_proc_mmap = vm_kmem_size / sizeof(struct vm_map_entry); max_proc_mmap /= 100; } static int vm_mmap_vnode(struct thread *, vm_size_t, vm_prot_t, vm_prot_t *, int *, struct vnode *, vm_ooffset_t, vm_object_t *); /* * MPSAFE */ /* ARGSUSED */ int sbrk(td, uap) struct thread *td; struct sbrk_args *uap; { /* Not yet implemented */ /* mtx_lock(&Giant); */ /* mtx_unlock(&Giant); */ return (EOPNOTSUPP); } #ifndef _SYS_SYSPROTO_H_ struct sstk_args { int incr; }; #endif /* * MPSAFE */ /* ARGSUSED */ int sstk(td, uap) struct thread *td; struct sstk_args *uap; { /* Not yet implemented */ /* mtx_lock(&Giant); */ /* mtx_unlock(&Giant); */ return (EOPNOTSUPP); } -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +#if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct getpagesize_args { int dummy; }; #endif /* ARGSUSED */ int ogetpagesize(td, uap) struct thread *td; struct getpagesize_args *uap; { /* MP SAFE */ td->td_retval[0] = PAGE_SIZE; return (0); } -#endif /* COMPAT_43 || COMPAT_SUNOS */ +#endif /* COMPAT_43 */ /* * Memory Map (mmap) system call. Note that the file offset * and address are allowed to be NOT page aligned, though if * the MAP_FIXED flag it set, both must have the same remainder * modulo the PAGE_SIZE (POSIX 1003.1b). If the address is not * page-aligned, the actual mapping starts at trunc_page(addr) * and the return value is adjusted up by the page offset. * * Generally speaking, only character devices which are themselves * memory-based, such as a video framebuffer, can be mmap'd. Otherwise * there would be no cache coherency between a descriptor and a VM mapping * both to the same character device. * * Block devices can be mmap'd no matter what they represent. Cache coherency * is maintained as long as you do not write directly to the underlying * character device. */ #ifndef _SYS_SYSPROTO_H_ struct mmap_args { void *addr; size_t len; int prot; int flags; int fd; long pad; off_t pos; }; #endif /* * MPSAFE */ int mmap(td, uap) struct thread *td; struct mmap_args *uap; { struct file *fp; struct vnode *vp; vm_offset_t addr; vm_size_t size, pageoff; vm_prot_t prot, maxprot; void *handle; int flags, error; off_t pos; struct vmspace *vms = td->td_proc->p_vmspace; addr = (vm_offset_t) uap->addr; size = uap->len; prot = uap->prot & VM_PROT_ALL; flags = uap->flags; pos = uap->pos; fp = NULL; /* make sure mapping fits into numeric range etc */ if ((ssize_t) uap->len < 0 || ((flags & MAP_ANON) && uap->fd != -1)) return (EINVAL); if (flags & MAP_STACK) { if ((uap->fd != -1) || ((prot & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE))) return (EINVAL); flags |= MAP_ANON; pos = 0; } /* * Align the file position to a page boundary, * and save its page offset component. */ pageoff = (pos & PAGE_MASK); pos -= pageoff; /* Adjust size for rounding (on both ends). */ size += pageoff; /* low end... */ size = (vm_size_t) round_page(size); /* hi end */ /* * Check for illegal addresses. Watch out for address wrap... Note * that VM_*_ADDRESS are not constants due to casts (argh). */ if (flags & MAP_FIXED) { /* * The specified address must have the same remainder * as the file offset taken modulo PAGE_SIZE, so it * should be aligned after adjustment by pageoff. */ addr -= pageoff; if (addr & PAGE_MASK) return (EINVAL); /* Address range must be all in user VM space. */ if (addr < vm_map_min(&vms->vm_map) || addr + size > vm_map_max(&vms->vm_map)) return (EINVAL); if (addr + size < addr) return (EINVAL); } else { /* * XXX for non-fixed mappings where no hint is provided or * the hint would fall in the potential heap space, * place it after the end of the largest possible heap. * * There should really be a pmap call to determine a reasonable * location. */ PROC_LOCK(td->td_proc); if (addr == 0 || (addr >= round_page((vm_offset_t)vms->vm_taddr) && addr < round_page((vm_offset_t)vms->vm_daddr + lim_max(td->td_proc, RLIMIT_DATA)))) addr = round_page((vm_offset_t)vms->vm_daddr + lim_max(td->td_proc, RLIMIT_DATA)); PROC_UNLOCK(td->td_proc); } if (flags & MAP_ANON) { /* * Mapping blank space is trivial. */ handle = NULL; maxprot = VM_PROT_ALL; pos = 0; } else { /* * Mapping file, get fp for validation. Obtain vnode and make * sure it is of appropriate type. * don't let the descriptor disappear on us if we block */ if ((error = fget(td, uap->fd, &fp)) != 0) goto done; if (fp->f_type != DTYPE_VNODE) { error = EINVAL; goto done; } /* * POSIX shared-memory objects are defined to have * kernel persistence, and are not defined to support * read(2)/write(2) -- or even open(2). Thus, we can * use MAP_ASYNC to trade on-disk coherence for speed. * The shm_open(3) library routine turns on the FPOSIXSHM * flag to request this behavior. */ if (fp->f_flag & FPOSIXSHM) flags |= MAP_NOSYNC; vp = fp->f_vnode; /* * Ensure that file and memory protections are * compatible. Note that we only worry about * writability if mapping is shared; in this case, * current and max prot are dictated by the open file. * XXX use the vnode instead? Problem is: what * credentials do we use for determination? What if * proc does a setuid? */ if (vp->v_mount != NULL && vp->v_mount->mnt_flag & MNT_NOEXEC) maxprot = VM_PROT_NONE; else maxprot = VM_PROT_EXECUTE; if (fp->f_flag & FREAD) { maxprot |= VM_PROT_READ; } else if (prot & PROT_READ) { error = EACCES; goto done; } /* * If we are sharing potential changes (either via * MAP_SHARED or via the implicit sharing of character * device mappings), and we are trying to get write * permission although we opened it without asking * for it, bail out. */ if ((flags & MAP_SHARED) != 0) { if ((fp->f_flag & FWRITE) != 0) { maxprot |= VM_PROT_WRITE; } else if ((prot & PROT_WRITE) != 0) { error = EACCES; goto done; } } else if (vp->v_type != VCHR || (fp->f_flag & FWRITE) != 0) { maxprot |= VM_PROT_WRITE; } handle = (void *)vp; } /* * Do not allow more then a certain number of vm_map_entry structures * per process. Scale with the number of rforks sharing the map * to make the limit reasonable for threads. */ if (max_proc_mmap && vms->vm_map.nentries >= max_proc_mmap * vms->vm_refcnt) { error = ENOMEM; goto done; } error = vm_mmap(&vms->vm_map, &addr, size, prot, maxprot, flags, handle, pos); if (error == 0) td->td_retval[0] = (register_t) (addr + pageoff); done: if (fp) fdrop(fp, td); return (error); } #ifdef COMPAT_43 #ifndef _SYS_SYSPROTO_H_ struct ommap_args { caddr_t addr; int len; int prot; int flags; int fd; long pos; }; #endif int ommap(td, uap) struct thread *td; struct ommap_args *uap; { struct mmap_args nargs; static const char cvtbsdprot[8] = { 0, PROT_EXEC, PROT_WRITE, PROT_EXEC | PROT_WRITE, PROT_READ, PROT_EXEC | PROT_READ, PROT_WRITE | PROT_READ, PROT_EXEC | PROT_WRITE | PROT_READ, }; #define OMAP_ANON 0x0002 #define OMAP_COPY 0x0020 #define OMAP_SHARED 0x0010 #define OMAP_FIXED 0x0100 nargs.addr = uap->addr; nargs.len = uap->len; nargs.prot = cvtbsdprot[uap->prot & 0x7]; nargs.flags = 0; if (uap->flags & OMAP_ANON) nargs.flags |= MAP_ANON; if (uap->flags & OMAP_COPY) nargs.flags |= MAP_COPY; if (uap->flags & OMAP_SHARED) nargs.flags |= MAP_SHARED; else nargs.flags |= MAP_PRIVATE; if (uap->flags & OMAP_FIXED) nargs.flags |= MAP_FIXED; nargs.fd = uap->fd; nargs.pos = uap->pos; return (mmap(td, &nargs)); } #endif /* COMPAT_43 */ #ifndef _SYS_SYSPROTO_H_ struct msync_args { void *addr; int len; int flags; }; #endif /* * MPSAFE */ int msync(td, uap) struct thread *td; struct msync_args *uap; { vm_offset_t addr; vm_size_t size, pageoff; int flags; vm_map_t map; int rv; addr = (vm_offset_t) uap->addr; size = uap->len; flags = uap->flags; pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = (vm_size_t) round_page(size); if (addr + size < addr) return (EINVAL); if ((flags & (MS_ASYNC|MS_INVALIDATE)) == (MS_ASYNC|MS_INVALIDATE)) return (EINVAL); map = &td->td_proc->p_vmspace->vm_map; /* * Clean the pages and interpret the return value. */ rv = vm_map_sync(map, addr, addr + size, (flags & MS_ASYNC) == 0, (flags & MS_INVALIDATE) != 0); switch (rv) { case KERN_SUCCESS: return (0); case KERN_INVALID_ADDRESS: return (EINVAL); /* Sun returns ENOMEM? */ case KERN_INVALID_ARGUMENT: return (EBUSY); default: return (EINVAL); } } #ifndef _SYS_SYSPROTO_H_ struct munmap_args { void *addr; size_t len; }; #endif /* * MPSAFE */ int munmap(td, uap) struct thread *td; struct munmap_args *uap; { vm_offset_t addr; vm_size_t size, pageoff; vm_map_t map; addr = (vm_offset_t) uap->addr; size = uap->len; if (size == 0) return (EINVAL); pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = (vm_size_t) round_page(size); if (addr + size < addr) return (EINVAL); /* * Check for illegal addresses. Watch out for address wrap... */ map = &td->td_proc->p_vmspace->vm_map; if (addr < vm_map_min(map) || addr + size > vm_map_max(map)) return (EINVAL); vm_map_lock(map); /* * Make sure entire range is allocated. */ if (!vm_map_check_protection(map, addr, addr + size, VM_PROT_NONE)) { vm_map_unlock(map); return (EINVAL); } /* returns nothing but KERN_SUCCESS anyway */ vm_map_delete(map, addr, addr + size); vm_map_unlock(map); return (0); } #ifndef _SYS_SYSPROTO_H_ struct mprotect_args { const void *addr; size_t len; int prot; }; #endif /* * MPSAFE */ int mprotect(td, uap) struct thread *td; struct mprotect_args *uap; { vm_offset_t addr; vm_size_t size, pageoff; vm_prot_t prot; addr = (vm_offset_t) uap->addr; size = uap->len; prot = uap->prot & VM_PROT_ALL; #if defined(VM_PROT_READ_IS_EXEC) if (prot & VM_PROT_READ) prot |= VM_PROT_EXECUTE; #endif pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = (vm_size_t) round_page(size); if (addr + size < addr) return (EINVAL); switch (vm_map_protect(&td->td_proc->p_vmspace->vm_map, addr, addr + size, prot, FALSE)) { case KERN_SUCCESS: return (0); case KERN_PROTECTION_FAILURE: return (EACCES); } return (EINVAL); } #ifndef _SYS_SYSPROTO_H_ struct minherit_args { void *addr; size_t len; int inherit; }; #endif /* * MPSAFE */ int minherit(td, uap) struct thread *td; struct minherit_args *uap; { vm_offset_t addr; vm_size_t size, pageoff; vm_inherit_t inherit; addr = (vm_offset_t)uap->addr; size = uap->len; inherit = uap->inherit; pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = (vm_size_t) round_page(size); if (addr + size < addr) return (EINVAL); switch (vm_map_inherit(&td->td_proc->p_vmspace->vm_map, addr, addr + size, inherit)) { case KERN_SUCCESS: return (0); case KERN_PROTECTION_FAILURE: return (EACCES); } return (EINVAL); } #ifndef _SYS_SYSPROTO_H_ struct madvise_args { void *addr; size_t len; int behav; }; #endif /* * MPSAFE */ /* ARGSUSED */ int madvise(td, uap) struct thread *td; struct madvise_args *uap; { vm_offset_t start, end; vm_map_t map; struct proc *p; int error; /* * Check for our special case, advising the swap pager we are * "immortal." */ if (uap->behav == MADV_PROTECT) { error = suser(td); if (error == 0) { p = td->td_proc; PROC_LOCK(p); p->p_flag |= P_PROTECTED; PROC_UNLOCK(p); } return (error); } /* * Check for illegal behavior */ if (uap->behav < 0 || uap->behav > MADV_CORE) return (EINVAL); /* * Check for illegal addresses. Watch out for address wrap... Note * that VM_*_ADDRESS are not constants due to casts (argh). */ map = &td->td_proc->p_vmspace->vm_map; if ((vm_offset_t)uap->addr < vm_map_min(map) || (vm_offset_t)uap->addr + uap->len > vm_map_max(map)) return (EINVAL); if (((vm_offset_t) uap->addr + uap->len) < (vm_offset_t) uap->addr) return (EINVAL); /* * Since this routine is only advisory, we default to conservative * behavior. */ start = trunc_page((vm_offset_t) uap->addr); end = round_page((vm_offset_t) uap->addr + uap->len); if (vm_map_madvise(map, start, end, uap->behav)) return (EINVAL); return (0); } #ifndef _SYS_SYSPROTO_H_ struct mincore_args { const void *addr; size_t len; char *vec; }; #endif /* * MPSAFE */ /* ARGSUSED */ int mincore(td, uap) struct thread *td; struct mincore_args *uap; { vm_offset_t addr, first_addr; vm_offset_t end, cend; pmap_t pmap; vm_map_t map; char *vec; int error = 0; int vecindex, lastvecindex; vm_map_entry_t current; vm_map_entry_t entry; int mincoreinfo; unsigned int timestamp; /* * Make sure that the addresses presented are valid for user * mode. */ first_addr = addr = trunc_page((vm_offset_t) uap->addr); end = addr + (vm_size_t)round_page(uap->len); map = &td->td_proc->p_vmspace->vm_map; if (end > vm_map_max(map) || end < addr) return (EINVAL); /* * Address of byte vector */ vec = uap->vec; pmap = vmspace_pmap(td->td_proc->p_vmspace); vm_map_lock_read(map); RestartScan: timestamp = map->timestamp; if (!vm_map_lookup_entry(map, addr, &entry)) entry = entry->next; /* * Do this on a map entry basis so that if the pages are not * in the current processes address space, we can easily look * up the pages elsewhere. */ lastvecindex = -1; for (current = entry; (current != &map->header) && (current->start < end); current = current->next) { /* * ignore submaps (for now) or null objects */ if ((current->eflags & MAP_ENTRY_IS_SUB_MAP) || current->object.vm_object == NULL) continue; /* * limit this scan to the current map entry and the * limits for the mincore call */ if (addr < current->start) addr = current->start; cend = current->end; if (cend > end) cend = end; /* * scan this entry one page at a time */ while (addr < cend) { /* * Check pmap first, it is likely faster, also * it can provide info as to whether we are the * one referencing or modifying the page. */ mtx_lock(&Giant); mincoreinfo = pmap_mincore(pmap, addr); mtx_unlock(&Giant); if (!mincoreinfo) { vm_pindex_t pindex; vm_ooffset_t offset; vm_page_t m; /* * calculate the page index into the object */ offset = current->offset + (addr - current->start); pindex = OFF_TO_IDX(offset); VM_OBJECT_LOCK(current->object.vm_object); m = vm_page_lookup(current->object.vm_object, pindex); /* * if the page is resident, then gather information about * it. */ if (m != NULL && m->valid != 0) { mincoreinfo = MINCORE_INCORE; vm_page_lock_queues(); if (m->dirty || pmap_is_modified(m)) mincoreinfo |= MINCORE_MODIFIED_OTHER; if ((m->flags & PG_REFERENCED) || pmap_ts_referenced(m)) { vm_page_flag_set(m, PG_REFERENCED); mincoreinfo |= MINCORE_REFERENCED_OTHER; } vm_page_unlock_queues(); } VM_OBJECT_UNLOCK(current->object.vm_object); } /* * subyte may page fault. In case it needs to modify * the map, we release the lock. */ vm_map_unlock_read(map); /* * calculate index into user supplied byte vector */ vecindex = OFF_TO_IDX(addr - first_addr); /* * If we have skipped map entries, we need to make sure that * the byte vector is zeroed for those skipped entries. */ while ((lastvecindex + 1) < vecindex) { error = subyte(vec + lastvecindex, 0); if (error) { error = EFAULT; goto done2; } ++lastvecindex; } /* * Pass the page information to the user */ error = subyte(vec + vecindex, mincoreinfo); if (error) { error = EFAULT; goto done2; } /* * If the map has changed, due to the subyte, the previous * output may be invalid. */ vm_map_lock_read(map); if (timestamp != map->timestamp) goto RestartScan; lastvecindex = vecindex; addr += PAGE_SIZE; } } /* * subyte may page fault. In case it needs to modify * the map, we release the lock. */ vm_map_unlock_read(map); /* * Zero the last entries in the byte vector. */ vecindex = OFF_TO_IDX(end - first_addr); while ((lastvecindex + 1) < vecindex) { error = subyte(vec + lastvecindex, 0); if (error) { error = EFAULT; goto done2; } ++lastvecindex; } /* * If the map has changed, due to the subyte, the previous * output may be invalid. */ vm_map_lock_read(map); if (timestamp != map->timestamp) goto RestartScan; vm_map_unlock_read(map); done2: return (error); } #ifndef _SYS_SYSPROTO_H_ struct mlock_args { const void *addr; size_t len; }; #endif /* * MPSAFE */ int mlock(td, uap) struct thread *td; struct mlock_args *uap; { struct proc *proc; vm_offset_t addr, end, last, start; vm_size_t npages, size; int error; error = suser(td); if (error) return (error); addr = (vm_offset_t)uap->addr; size = uap->len; last = addr + size; start = trunc_page(addr); end = round_page(last); if (last < addr || end < addr) return (EINVAL); npages = atop(end - start); if (npages > vm_page_max_wired) return (ENOMEM); proc = td->td_proc; PROC_LOCK(proc); if (ptoa(npages + pmap_wired_count(vm_map_pmap(&proc->p_vmspace->vm_map))) > lim_cur(proc, RLIMIT_MEMLOCK)) { PROC_UNLOCK(proc); return (ENOMEM); } PROC_UNLOCK(proc); if (npages + cnt.v_wire_count > vm_page_max_wired) return (EAGAIN); error = vm_map_wire(&proc->p_vmspace->vm_map, start, end, VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES); return (error == KERN_SUCCESS ? 0 : ENOMEM); } #ifndef _SYS_SYSPROTO_H_ struct mlockall_args { int how; }; #endif /* * MPSAFE */ int mlockall(td, uap) struct thread *td; struct mlockall_args *uap; { vm_map_t map; int error; map = &td->td_proc->p_vmspace->vm_map; error = 0; if ((uap->how == 0) || ((uap->how & ~(MCL_CURRENT|MCL_FUTURE)) != 0)) return (EINVAL); #if 0 /* * If wiring all pages in the process would cause it to exceed * a hard resource limit, return ENOMEM. */ PROC_LOCK(td->td_proc); if (map->size - ptoa(pmap_wired_count(vm_map_pmap(map)) > lim_cur(td->td_proc, RLIMIT_MEMLOCK))) { PROC_UNLOCK(td->td_proc); return (ENOMEM); } PROC_UNLOCK(td->td_proc); #else error = suser(td); if (error) return (error); #endif if (uap->how & MCL_FUTURE) { vm_map_lock(map); vm_map_modflags(map, MAP_WIREFUTURE, 0); vm_map_unlock(map); error = 0; } if (uap->how & MCL_CURRENT) { /* * P1003.1-2001 mandates that all currently mapped pages * will be memory resident and locked (wired) upon return * from mlockall(). vm_map_wire() will wire pages, by * calling vm_fault_wire() for each page in the region. */ error = vm_map_wire(map, vm_map_min(map), vm_map_max(map), VM_MAP_WIRE_USER|VM_MAP_WIRE_HOLESOK); error = (error == KERN_SUCCESS ? 0 : EAGAIN); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct munlockall_args { register_t dummy; }; #endif /* * MPSAFE */ int munlockall(td, uap) struct thread *td; struct munlockall_args *uap; { vm_map_t map; int error; map = &td->td_proc->p_vmspace->vm_map; error = suser(td); if (error) return (error); /* Clear the MAP_WIREFUTURE flag from this vm_map. */ vm_map_lock(map); vm_map_modflags(map, 0, MAP_WIREFUTURE); vm_map_unlock(map); /* Forcibly unwire all pages. */ error = vm_map_unwire(map, vm_map_min(map), vm_map_max(map), VM_MAP_WIRE_USER|VM_MAP_WIRE_HOLESOK); return (error); } #ifndef _SYS_SYSPROTO_H_ struct munlock_args { const void *addr; size_t len; }; #endif /* * MPSAFE */ int munlock(td, uap) struct thread *td; struct munlock_args *uap; { vm_offset_t addr, end, last, start; vm_size_t size; int error; error = suser(td); if (error) return (error); addr = (vm_offset_t)uap->addr; size = uap->len; last = addr + size; start = trunc_page(addr); end = round_page(last); if (last < addr || end < addr) return (EINVAL); error = vm_map_unwire(&td->td_proc->p_vmspace->vm_map, start, end, VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES); return (error == KERN_SUCCESS ? 0 : ENOMEM); } /* * vm_mmap_vnode() * * MPSAFE * * Helper function for vm_mmap. Perform sanity check specific for mmap * operations on vnodes. */ int vm_mmap_vnode(struct thread *td, vm_size_t objsize, vm_prot_t prot, vm_prot_t *maxprotp, int *flagsp, struct vnode *vp, vm_ooffset_t foff, vm_object_t *objp) { struct vattr va; void *handle; vm_object_t obj; int disablexworkaround, error, flags, type; mtx_lock(&Giant); if ((error = vget(vp, LK_EXCLUSIVE, td)) != 0) { mtx_unlock(&Giant); return (error); } flags = *flagsp; if (vp->v_type == VREG) { /* * Get the proper underlying object */ if (VOP_GETVOBJECT(vp, &obj) != 0) { error = EINVAL; goto done; } if (obj->handle != vp) { vput(vp); vp = (struct vnode*)obj->handle; vget(vp, LK_EXCLUSIVE, td); } type = OBJT_VNODE; handle = vp; } else if (vp->v_type == VCHR) { type = OBJT_DEVICE; handle = vp->v_rdev; if(vp->v_rdev->si_devsw->d_flags & D_MMAP_ANON) { *maxprotp = VM_PROT_ALL; *flagsp |= MAP_ANON; error = 0; goto done; } /* * cdevs does not provide private mappings of any kind. */ if ((*maxprotp & VM_PROT_WRITE) == 0 && (prot & PROT_WRITE) != 0) { error = EACCES; goto done; } /* * However, for XIG X server to continue to work, * we should allow the superuser to do it anyway. * We only allow it at securelevel < 1. * (Because the XIG X server writes directly to video * memory via /dev/mem, it should never work at any * other securelevel. * XXX this will have to go */ if (securelevel_ge(td->td_ucred, 1)) disablexworkaround = 1; else disablexworkaround = suser(td); if (disablexworkaround && (flags & (MAP_PRIVATE|MAP_COPY))) { error = EINVAL; goto done; } /* * Force device mappings to be shared. */ flags &= ~(MAP_PRIVATE|MAP_COPY); flags |= MAP_SHARED; } else { error = EINVAL; goto done; } if ((error = VOP_GETATTR(vp, &va, td->td_ucred, td))) { goto done; } if ((flags & MAP_SHARED) != 0) { if ((va.va_flags & (SF_SNAPSHOT|IMMUTABLE|APPEND)) != 0) { if (prot & PROT_WRITE) { error = EPERM; goto done; } *maxprotp &= ~VM_PROT_WRITE; } #ifdef MAC error = mac_check_vnode_mmap(td->td_ucred, vp, prot); if (error != 0) goto done; #endif } /* * If it is a regular file without any references * we do not need to sync it. * Adjust object size to be the size of actual file. */ if (vp->v_type == VREG) { objsize = round_page(va.va_size); if (va.va_nlink == 0) flags |= MAP_NOSYNC; } obj = vm_pager_allocate(type, handle, objsize, prot, foff); if (obj == NULL) { error = (type == OBJT_DEVICE ? EINVAL : ENOMEM); goto done; } *objp = obj; *flagsp = flags; done: vput(vp); mtx_unlock(&Giant); return (error); } /* * vm_mmap() * * MPSAFE * * Internal version of mmap. Currently used by mmap, exec, and sys5 * shared memory. Handle is either a vnode pointer or NULL for MAP_ANON. */ int vm_mmap(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot, vm_prot_t maxprot, int flags, void *handle, vm_ooffset_t foff) { boolean_t fitit; vm_object_t object; int rv = KERN_SUCCESS; vm_ooffset_t objsize; int docow, error; struct thread *td = curthread; if (size == 0) return (0); objsize = size = round_page(size); PROC_LOCK(td->td_proc); if (td->td_proc->p_vmspace->vm_map.size + size > lim_cur(td->td_proc, RLIMIT_VMEM)) { PROC_UNLOCK(td->td_proc); return(ENOMEM); } PROC_UNLOCK(td->td_proc); /* * We currently can only deal with page aligned file offsets. * The check is here rather than in the syscall because the * kernel calls this function internally for other mmaping * operations (such as in exec) and non-aligned offsets will * cause pmap inconsistencies...so we want to be sure to * disallow this in all cases. */ if (foff & PAGE_MASK) return (EINVAL); if ((flags & MAP_FIXED) == 0) { fitit = TRUE; *addr = round_page(*addr); } else { if (*addr != trunc_page(*addr)) return (EINVAL); fitit = FALSE; (void) vm_map_remove(map, *addr, *addr + size); } /* * Lookup/allocate object. */ if (handle != NULL) { error = vm_mmap_vnode(td, size, prot, &maxprot, &flags, handle, foff, &object); if (error) { return (error); } } if (flags & MAP_ANON) { object = NULL; docow = 0; /* * Unnamed anonymous regions always start at 0. */ if (handle == 0) foff = 0; } else { docow = MAP_PREFAULT_PARTIAL; } if ((flags & (MAP_ANON|MAP_SHARED)) == 0) docow |= MAP_COPY_ON_WRITE; if (flags & MAP_NOSYNC) docow |= MAP_DISABLE_SYNCER; if (flags & MAP_NOCORE) docow |= MAP_DISABLE_COREDUMP; #if defined(VM_PROT_READ_IS_EXEC) if (prot & VM_PROT_READ) prot |= VM_PROT_EXECUTE; if (maxprot & VM_PROT_READ) maxprot |= VM_PROT_EXECUTE; #endif if (fitit) *addr = pmap_addr_hint(object, *addr, size); if (flags & MAP_STACK) rv = vm_map_stack(map, *addr, size, prot, maxprot, docow | MAP_STACK_GROWS_DOWN); else rv = vm_map_find(map, object, foff, addr, size, fitit, prot, maxprot, docow); if (rv != KERN_SUCCESS) { /* * Lose the object reference. Will destroy the * object if it's an unnamed anonymous mapping * or named anonymous without other references. */ vm_object_deallocate(object); } else if (flags & MAP_SHARED) { /* * Shared memory is also shared with children. */ rv = vm_map_inherit(map, *addr, *addr + size, VM_INHERIT_SHARE); if (rv != KERN_SUCCESS) (void) vm_map_remove(map, *addr, *addr + size); } /* * If the process has requested that all future mappings * be wired, then heed this. */ if ((rv == KERN_SUCCESS) && (map->flags & MAP_WIREFUTURE)) vm_map_wire(map, *addr, *addr + size, VM_MAP_WIRE_USER|VM_MAP_WIRE_NOHOLES); switch (rv) { case KERN_SUCCESS: return (0); case KERN_INVALID_ADDRESS: case KERN_NO_SPACE: return (ENOMEM); case KERN_PROTECTION_FAILURE: return (EACCES); default: return (EINVAL); } }