Index: projects/release-pkg/lib/libc/gen/elf_utils.c =================================================================== --- projects/release-pkg/lib/libc/gen/elf_utils.c (revision 295422) +++ projects/release-pkg/lib/libc/gen/elf_utils.c (revision 295423) @@ -1,77 +1,84 @@ /* * Copyright (c) 2010 Konstantin Belousov * 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. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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 #include #include #include #include #include +#include "libc_private.h" int __elf_phdr_match_addr(struct dl_phdr_info *, void *); void __pthread_map_stacks_exec(void); int __elf_phdr_match_addr(struct dl_phdr_info *phdr_info, void *addr) { const Elf_Phdr *ph; int i; for (i = 0; i < phdr_info->dlpi_phnum; i++) { ph = &phdr_info->dlpi_phdr[i]; if (ph->p_type != PT_LOAD || (ph->p_flags & PF_X) == 0) continue; if (phdr_info->dlpi_addr + ph->p_vaddr <= (uintptr_t)addr && (uintptr_t)addr + sizeof(addr) < phdr_info->dlpi_addr + ph->p_vaddr + ph->p_memsz) break; } return (i != phdr_info->dlpi_phnum); } -#pragma weak __pthread_map_stacks_exec void -__pthread_map_stacks_exec(void) +__libc_map_stacks_exec(void) { int mib[2]; struct rlimit rlim; u_long usrstack; size_t len; mib[0] = CTL_KERN; mib[1] = KERN_USRSTACK; len = sizeof(usrstack); if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &usrstack, &len, NULL, 0) == -1) return; if (getrlimit(RLIMIT_STACK, &rlim) == -1) return; mprotect((void *)(uintptr_t)(usrstack - rlim.rlim_cur), rlim.rlim_cur, _rtld_get_stack_prot()); } +#pragma weak __pthread_map_stacks_exec +void +__pthread_map_stacks_exec(void) +{ + + ((void (*)(void))__libc_interposing[INTERPOS_map_stacks_exec])(); +} Index: projects/release-pkg/lib/libc/include/libc_private.h =================================================================== --- projects/release-pkg/lib/libc/include/libc_private.h (revision 295422) +++ projects/release-pkg/lib/libc/include/libc_private.h (revision 295423) @@ -1,388 +1,390 @@ /* * Copyright (c) 1998 John Birrell . * 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. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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$ * * Private definitions for libc, libc_r and libpthread. * */ #ifndef _LIBC_PRIVATE_H_ #define _LIBC_PRIVATE_H_ #include #include /* * This global flag is non-zero when a process has created one * or more threads. It is used to avoid calling locking functions * when they are not required. */ extern int __isthreaded; /* * Elf_Auxinfo *__elf_aux_vector, the pointer to the ELF aux vector * provided by kernel. Either set for us by rtld, or found at runtime * on stack for static binaries. * * Type is void to avoid polluting whole libc with ELF types. */ extern void *__elf_aux_vector; /* * libc should use libc_dlopen internally, which respects a global * flag where loading of new shared objects can be restricted. */ void *libc_dlopen(const char *, int); /* * For dynamic linker. */ void _rtld_error(const char *fmt, ...); /* * File lock contention is difficult to diagnose without knowing * where locks were set. Allow a debug library to be built which * records the source file and line number of each lock call. */ #ifdef _FLOCK_DEBUG #define _FLOCKFILE(x) _flockfile_debug(x, __FILE__, __LINE__) #else #define _FLOCKFILE(x) _flockfile(x) #endif /* * Macros for locking and unlocking FILEs. These test if the * process is threaded to avoid locking when not required. */ #define FLOCKFILE(fp) if (__isthreaded) _FLOCKFILE(fp) #define FUNLOCKFILE(fp) if (__isthreaded) _funlockfile(fp) struct _spinlock; extern struct _spinlock __stdio_thread_lock __hidden; #define STDIO_THREAD_LOCK() \ do { \ if (__isthreaded) \ _SPINLOCK(&__stdio_thread_lock); \ } while (0) #define STDIO_THREAD_UNLOCK() \ do { \ if (__isthreaded) \ _SPINUNLOCK(&__stdio_thread_lock); \ } while (0) void __libc_spinlock_stub(struct _spinlock *); void __libc_spinunlock_stub(struct _spinlock *); /* * Indexes into the pthread jump table. * * Warning! If you change this type, you must also change the threads * libraries that reference it (libc_r, libpthread). */ typedef enum { PJT_ATFORK, PJT_ATTR_DESTROY, PJT_ATTR_GETDETACHSTATE, PJT_ATTR_GETGUARDSIZE, PJT_ATTR_GETINHERITSCHED, PJT_ATTR_GETSCHEDPARAM, PJT_ATTR_GETSCHEDPOLICY, PJT_ATTR_GETSCOPE, PJT_ATTR_GETSTACKADDR, PJT_ATTR_GETSTACKSIZE, PJT_ATTR_INIT, PJT_ATTR_SETDETACHSTATE, PJT_ATTR_SETGUARDSIZE, PJT_ATTR_SETINHERITSCHED, PJT_ATTR_SETSCHEDPARAM, PJT_ATTR_SETSCHEDPOLICY, PJT_ATTR_SETSCOPE, PJT_ATTR_SETSTACKADDR, PJT_ATTR_SETSTACKSIZE, PJT_CANCEL, PJT_CLEANUP_POP, PJT_CLEANUP_PUSH, PJT_COND_BROADCAST, PJT_COND_DESTROY, PJT_COND_INIT, PJT_COND_SIGNAL, PJT_COND_TIMEDWAIT, PJT_COND_WAIT, PJT_DETACH, PJT_EQUAL, PJT_EXIT, PJT_GETSPECIFIC, PJT_JOIN, PJT_KEY_CREATE, PJT_KEY_DELETE, PJT_KILL, PJT_MAIN_NP, PJT_MUTEXATTR_DESTROY, PJT_MUTEXATTR_INIT, PJT_MUTEXATTR_SETTYPE, PJT_MUTEX_DESTROY, PJT_MUTEX_INIT, PJT_MUTEX_LOCK, PJT_MUTEX_TRYLOCK, PJT_MUTEX_UNLOCK, PJT_ONCE, PJT_RWLOCK_DESTROY, PJT_RWLOCK_INIT, PJT_RWLOCK_RDLOCK, PJT_RWLOCK_TRYRDLOCK, PJT_RWLOCK_TRYWRLOCK, PJT_RWLOCK_UNLOCK, PJT_RWLOCK_WRLOCK, PJT_SELF, PJT_SETCANCELSTATE, PJT_SETCANCELTYPE, PJT_SETSPECIFIC, PJT_SIGMASK, PJT_TESTCANCEL, PJT_CLEANUP_POP_IMP, PJT_CLEANUP_PUSH_IMP, PJT_CANCEL_ENTER, PJT_CANCEL_LEAVE, PJT_MAX } pjt_index_t; typedef int (*pthread_func_t)(void); typedef pthread_func_t pthread_func_entry_t[2]; extern pthread_func_entry_t __thr_jtable[]; void __set_error_selector(int *(*arg)(void)); int _pthread_mutex_init_calloc_cb_stub(pthread_mutex_t *mutex, void *(calloc_cb)(__size_t, __size_t)); typedef int (*interpos_func_t)(void); interpos_func_t *__libc_interposing_slot(int interposno); extern interpos_func_t __libc_interposing[] __hidden; enum { INTERPOS_accept, INTERPOS_accept4, INTERPOS_aio_suspend, INTERPOS_close, INTERPOS_connect, INTERPOS_fcntl, INTERPOS_fsync, INTERPOS_fork, INTERPOS_msync, INTERPOS_nanosleep, INTERPOS_openat, INTERPOS_poll, INTERPOS_pselect, INTERPOS_recvfrom, INTERPOS_recvmsg, INTERPOS_select, INTERPOS_sendmsg, INTERPOS_sendto, INTERPOS_setcontext, INTERPOS_sigaction, INTERPOS_sigprocmask, INTERPOS_sigsuspend, INTERPOS_sigwait, INTERPOS_sigtimedwait, INTERPOS_sigwaitinfo, INTERPOS_swapcontext, INTERPOS_system, INTERPOS_tcdrain, INTERPOS_read, INTERPOS_readv, INTERPOS_wait4, INTERPOS_write, INTERPOS_writev, INTERPOS__pthread_mutex_init_calloc_cb, INTERPOS_spinlock, INTERPOS_spinunlock, INTERPOS_kevent, INTERPOS_wait6, INTERPOS_ppoll, + INTERPOS_map_stacks_exec, INTERPOS_MAX }; /* * yplib internal interfaces */ #ifdef YP int _yp_check(char **); #endif /* * Initialise TLS for static programs */ void _init_tls(void); /* * Provides pthread_once()-like functionality for both single-threaded * and multi-threaded applications. */ int _once(pthread_once_t *, void (*)(void)); /* * Set the TLS thread pointer */ void _set_tp(void *tp); /* * This is a pointer in the C run-time startup code. It is used * by getprogname() and setprogname(). */ extern const char *__progname; /* * This function is used by the threading libraries to notify malloc that a * thread is exiting. */ void _malloc_thread_cleanup(void); /* * These functions are used by the threading libraries in order to protect * malloc across fork(). */ void _malloc_prefork(void); void _malloc_postfork(void); void _malloc_first_thread(void); /* * Function to clean up streams, called from abort() and exit(). */ extern void (*__cleanup)(void) __hidden; /* * Get kern.osreldate to detect ABI revisions. Explicitly * ignores value of $OSVERSION and caches result. */ int __getosreldate(void); #include #include struct aiocb; struct fd_set; struct iovec; struct kevent; struct msghdr; struct pollfd; struct rusage; struct sigaction; struct sockaddr; struct timespec; struct timeval; struct timezone; struct __siginfo; struct __ucontext; struct __wrusage; enum idtype; int __sys_aio_suspend(const struct aiocb * const[], int, const struct timespec *); int __sys_accept(int, struct sockaddr *, __socklen_t *); int __sys_accept4(int, struct sockaddr *, __socklen_t *, int); int __sys_clock_gettime(__clockid_t, struct timespec *ts); int __sys_close(int); int __sys_connect(int, const struct sockaddr *, __socklen_t); int __sys_fcntl(int, int, ...); int __sys_fsync(int); __pid_t __sys_fork(void); int __sys_ftruncate(int, __off_t); int __sys_gettimeofday(struct timeval *, struct timezone *); int __sys_kevent(int, const struct kevent *, int, struct kevent *, int, const struct timespec *); __off_t __sys_lseek(int, __off_t, int); void *__sys_mmap(void *, __size_t, int, int, int, __off_t); int __sys_msync(void *, __size_t, int); int __sys_nanosleep(const struct timespec *, struct timespec *); int __sys_open(const char *, int, ...); int __sys_openat(int, const char *, int, ...); int __sys_pselect(int, struct fd_set *, struct fd_set *, struct fd_set *, const struct timespec *, const __sigset_t *); int __sys_poll(struct pollfd *, unsigned, int); int __sys_ppoll(struct pollfd *, unsigned, const struct timespec *, const __sigset_t *); __ssize_t __sys_pread(int, void *, __size_t, __off_t); __ssize_t __sys_pwrite(int, const void *, __size_t, __off_t); __ssize_t __sys_read(int, void *, __size_t); __ssize_t __sys_readv(int, const struct iovec *, int); __ssize_t __sys_recv(int, void *, __size_t, int); __ssize_t __sys_recvfrom(int, void *, __size_t, int, struct sockaddr *, __socklen_t *); __ssize_t __sys_recvmsg(int, struct msghdr *, int); int __sys_select(int, struct fd_set *, struct fd_set *, struct fd_set *, struct timeval *); __ssize_t __sys_sendmsg(int, const struct msghdr *, int); __ssize_t __sys_sendto(int, const void *, __size_t, int, const struct sockaddr *, __socklen_t); int __sys_setcontext(const struct __ucontext *); int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigprocmask(int, const __sigset_t *, __sigset_t *); int __sys_sigsuspend(const __sigset_t *); int __sys_sigtimedwait(const __sigset_t *, struct __siginfo *, const struct timespec *); int __sys_sigwait(const __sigset_t *, int *); int __sys_sigwaitinfo(const __sigset_t *, struct __siginfo *); int __sys_swapcontext(struct __ucontext *, const struct __ucontext *); int __sys_thr_kill(long, int); int __sys_thr_self(long *); int __sys_truncate(const char *, __off_t); __pid_t __sys_wait4(__pid_t, int *, int, struct rusage *); __pid_t __sys_wait6(enum idtype, __id_t, int *, int, struct __wrusage *, struct __siginfo *); __ssize_t __sys_write(int, const void *, __size_t); __ssize_t __sys_writev(int, const struct iovec *, int); int __libc_sigaction(int, const struct sigaction *, struct sigaction *) __hidden; int __libc_sigprocmask(int, const __sigset_t *, __sigset_t *) __hidden; int __libc_sigsuspend(const __sigset_t *) __hidden; int __libc_sigwait(const __sigset_t * __restrict, int * restrict sig); int __libc_system(const char *); int __libc_tcdrain(int); int __fcntl_compat(int fd, int cmd, ...); int __sys_futimens(int fd, const struct timespec *times) __hidden; int __sys_utimensat(int fd, const char *path, const struct timespec *times, int flag) __hidden; /* execve() with PATH processing to implement posix_spawnp() */ int _execvpe(const char *, char * const *, char * const *); int _elf_aux_info(int aux, void *buf, int buflen); struct dl_phdr_info; int __elf_phdr_match_addr(struct dl_phdr_info *, void *); void __init_elf_aux_vector(void); +void __libc_map_stacks_exec(void); void _pthread_cancel_enter(int); void _pthread_cancel_leave(int); #endif /* _LIBC_PRIVATE_H_ */ Index: projects/release-pkg/lib/libc/sys/interposing_table.c =================================================================== --- projects/release-pkg/lib/libc/sys/interposing_table.c (revision 295422) +++ projects/release-pkg/lib/libc/sys/interposing_table.c (revision 295423) @@ -1,89 +1,90 @@ /* * Copyright (c) 2014 The FreeBSD Foundation. * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * 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(s), this list of conditions and the following disclaimer as * the first lines of this file unmodified other than the possible * addition of one or more copyright notices. * 2. Redistributions in binary form must reproduce the above copyright * notice(s), 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 COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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 "libc_private.h" #define SLOT(a, b) \ [INTERPOS_##a] = (interpos_func_t)b interpos_func_t __libc_interposing[INTERPOS_MAX] = { SLOT(accept, __sys_accept), SLOT(accept4, __sys_accept4), SLOT(aio_suspend, __sys_aio_suspend), SLOT(close, __sys_close), SLOT(connect, __sys_connect), SLOT(fcntl, __sys_fcntl), SLOT(fsync, __sys_fsync), SLOT(fork, __sys_fork), SLOT(msync, __sys_msync), SLOT(nanosleep, __sys_nanosleep), SLOT(openat, __sys_openat), SLOT(poll, __sys_poll), SLOT(pselect, __sys_pselect), SLOT(read, __sys_read), SLOT(readv, __sys_readv), SLOT(recvfrom, __sys_recvfrom), SLOT(recvmsg, __sys_recvmsg), SLOT(select, __sys_select), SLOT(sendmsg, __sys_sendmsg), SLOT(sendto, __sys_sendto), SLOT(setcontext, __sys_setcontext), SLOT(sigaction, __sys_sigaction), SLOT(sigprocmask, __sys_sigprocmask), SLOT(sigsuspend, __sys_sigsuspend), SLOT(sigwait, __libc_sigwait), SLOT(sigtimedwait, __sys_sigtimedwait), SLOT(sigwaitinfo, __sys_sigwaitinfo), SLOT(swapcontext, __sys_swapcontext), SLOT(system, __libc_system), SLOT(tcdrain, __libc_tcdrain), SLOT(wait4, __sys_wait4), SLOT(write, __sys_write), SLOT(writev, __sys_writev), SLOT(_pthread_mutex_init_calloc_cb, _pthread_mutex_init_calloc_cb_stub), SLOT(spinlock, __libc_spinlock_stub), SLOT(spinunlock, __libc_spinunlock_stub), SLOT(kevent, __sys_kevent), SLOT(wait6, __sys_wait6), SLOT(ppoll, __sys_ppoll), + SLOT(map_stacks_exec, __libc_map_stacks_exec), }; #undef SLOT interpos_func_t * __libc_interposing_slot(int interposno) { return (&__libc_interposing[interposno]); } Index: projects/release-pkg/lib/libc =================================================================== --- projects/release-pkg/lib/libc (revision 295422) +++ projects/release-pkg/lib/libc (revision 295423) Property changes on: projects/release-pkg/lib/libc ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/lib/libc:r295394-295422 Index: projects/release-pkg/lib/libthr/pthread.map =================================================================== --- projects/release-pkg/lib/libthr/pthread.map (revision 295422) +++ projects/release-pkg/lib/libthr/pthread.map (revision 295423) @@ -1,319 +1,317 @@ /* * $FreeBSD$ */ /* * Use the same naming scheme as libc. */ FBSD_1.0 { pthread_atfork; pthread_barrier_destroy; pthread_barrier_init; pthread_barrier_wait; pthread_barrierattr_destroy; pthread_barrierattr_getpshared; pthread_barrierattr_init; pthread_barrierattr_setpshared; pthread_attr_destroy; pthread_attr_get_np; pthread_attr_getdetachstate; pthread_attr_getguardsize; pthread_attr_getinheritsched; pthread_attr_getschedparam; pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_getstack; pthread_attr_getstackaddr; pthread_attr_getstacksize; pthread_attr_init; pthread_attr_setcreatesuspend_np; pthread_attr_setdetachstate; pthread_attr_setguardsize; pthread_attr_setinheritsched; pthread_attr_setschedparam; pthread_attr_setschedpolicy; pthread_attr_setscope; pthread_attr_setstack; pthread_attr_setstackaddr; pthread_attr_setstacksize; pthread_cancel; pthread_cleanup_pop; pthread_cleanup_push; pthread_cond_broadcast; pthread_cond_destroy; pthread_cond_init; pthread_cond_signal; pthread_cond_timedwait; pthread_cond_wait; pthread_condattr_destroy; pthread_condattr_getclock; pthread_condattr_getpshared; pthread_condattr_init; pthread_condattr_setclock; pthread_condattr_setpshared; pthread_create; pthread_detach; pthread_equal; pthread_exit; pthread_getconcurrency; pthread_getprio; pthread_getschedparam; pthread_getspecific; pthread_join; pthread_key_create; pthread_key_delete; pthread_kill; pthread_main_np; pthread_multi_np; pthread_mutex_destroy; pthread_mutex_getprioceiling; pthread_mutex_init; pthread_mutex_lock; pthread_mutex_setprioceiling; pthread_mutex_timedlock; pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy; pthread_mutexattr_getkind_np; pthread_mutexattr_getprioceiling; pthread_mutexattr_getpshared; pthread_mutexattr_getprotocol; pthread_mutexattr_gettype; pthread_mutexattr_init; pthread_mutexattr_setkind_np; pthread_mutexattr_setprioceiling; pthread_mutexattr_setprotocol; pthread_mutexattr_setpshared; pthread_mutexattr_settype; pthread_once; pthread_resume_all_np; pthread_resume_np; pthread_rwlock_destroy; pthread_rwlock_init; pthread_rwlock_rdlock; pthread_rwlock_timedrdlock; pthread_rwlock_timedwrlock; pthread_rwlock_tryrdlock; pthread_rwlock_trywrlock; pthread_rwlock_unlock; pthread_rwlock_wrlock; pthread_rwlockattr_destroy; pthread_rwlockattr_getpshared; pthread_rwlockattr_init; pthread_rwlockattr_setpshared; pthread_set_name_np; pthread_self; pthread_setcancelstate; pthread_setcanceltype; pthread_setconcurrency; pthread_setprio; pthread_setschedparam; pthread_setspecific; pthread_sigmask; pthread_single_np; pthread_spin_destroy; pthread_spin_init; pthread_spin_lock; pthread_spin_trylock; pthread_spin_unlock; pthread_suspend_all_np; pthread_suspend_np; pthread_switch_add_np; pthread_switch_delete_np; pthread_testcancel; pthread_timedjoin_np; pthread_yield; }; /* * List the private interfaces reserved for use in FreeBSD libraries. * These are not part of our application ABI. */ FBSDprivate_1.0 { __pthread_cond_timedwait; __pthread_cond_wait; __pthread_cxa_finalize; __pthread_mutex_init; __pthread_mutex_lock; __pthread_mutex_timedlock; __pthread_mutex_trylock; _pthread_atfork; _pthread_barrier_destroy; _pthread_barrier_init; _pthread_barrier_wait; _pthread_barrierattr_destroy; _pthread_barrierattr_getpshared; _pthread_barrierattr_init; _pthread_barrierattr_setpshared; _pthread_attr_destroy; _pthread_attr_get_np; _pthread_attr_getaffinity_np; _pthread_attr_getdetachstate; _pthread_attr_getguardsize; _pthread_attr_getinheritsched; _pthread_attr_getschedparam; _pthread_attr_getschedpolicy; _pthread_attr_getscope; _pthread_attr_getstack; _pthread_attr_getstackaddr; _pthread_attr_getstacksize; _pthread_attr_init; _pthread_attr_setaffinity_np; _pthread_attr_setcreatesuspend_np; _pthread_attr_setdetachstate; _pthread_attr_setguardsize; _pthread_attr_setinheritsched; _pthread_attr_setschedparam; _pthread_attr_setschedpolicy; _pthread_attr_setscope; _pthread_attr_setstack; _pthread_attr_setstackaddr; _pthread_attr_setstacksize; _pthread_cancel; _pthread_cancel_enter; _pthread_cancel_leave; _pthread_cleanup_pop; _pthread_cleanup_push; _pthread_cond_broadcast; _pthread_cond_destroy; _pthread_cond_init; _pthread_cond_signal; _pthread_cond_timedwait; _pthread_cond_wait; _pthread_condattr_destroy; _pthread_condattr_getclock; _pthread_condattr_getpshared; _pthread_condattr_init; _pthread_condattr_setclock; _pthread_condattr_setpshared; _pthread_create; _pthread_detach; _pthread_equal; _pthread_exit; _pthread_getaffinity_np; _pthread_getconcurrency; _pthread_getcpuclockid; _pthread_getprio; _pthread_getschedparam; _pthread_getspecific; _pthread_getthreadid_np; _pthread_join; _pthread_key_create; _pthread_key_delete; _pthread_kill; _pthread_main_np; _pthread_multi_np; _pthread_mutex_destroy; _pthread_mutex_getprioceiling; _pthread_mutex_getspinloops_np; _pthread_mutex_getyieldloops_np; _pthread_mutex_init; _pthread_mutex_init_calloc_cb; _pthread_mutex_isowned_np; _pthread_mutex_lock; _pthread_mutex_setprioceiling; _pthread_mutex_setspinloops_np; _pthread_mutex_setyieldloops_np; _pthread_mutex_timedlock; _pthread_mutex_trylock; _pthread_mutex_unlock; _pthread_mutexattr_destroy; _pthread_mutexattr_getkind_np; _pthread_mutexattr_getprioceiling; _pthread_mutexattr_getprotocol; _pthread_mutexattr_getpshared; _pthread_mutexattr_gettype; _pthread_mutexattr_init; _pthread_mutexattr_setkind_np; _pthread_mutexattr_setprioceiling; _pthread_mutexattr_setprotocol; _pthread_mutexattr_setpshared; _pthread_mutexattr_settype; _pthread_once; _pthread_resume_all_np; _pthread_resume_np; _pthread_rwlock_destroy; _pthread_rwlock_init; _pthread_rwlock_rdlock; _pthread_rwlock_timedrdlock; _pthread_rwlock_timedwrlock; _pthread_rwlock_tryrdlock; _pthread_rwlock_trywrlock; _pthread_rwlock_unlock; _pthread_rwlock_wrlock; _pthread_rwlockattr_destroy; _pthread_rwlockattr_getpshared; _pthread_rwlockattr_init; _pthread_rwlockattr_setpshared; _pthread_self; _pthread_set_name_np; _pthread_setaffinity_np; _pthread_setcancelstate; _pthread_setcanceltype; _pthread_setconcurrency; _pthread_setprio; _pthread_setschedparam; _pthread_setspecific; _pthread_sigmask; _pthread_single_np; _pthread_spin_destroy; _pthread_spin_init; _pthread_spin_lock; _pthread_spin_trylock; _pthread_spin_unlock; _pthread_suspend_all_np; _pthread_suspend_np; _pthread_switch_add_np; _pthread_switch_delete_np; _pthread_testcancel; _pthread_timedjoin_np; _pthread_yield; /* Debugger needs these. */ _libthr_debug; _thread_active_threads; _thread_bp_create; _thread_bp_death; _thread_event_mask; _thread_keytable; _thread_last_event; _thread_list; _thread_max_keys; _thread_off_attr_flags; _thread_off_dtv; _thread_off_event_buf; _thread_off_event_mask; _thread_off_key_allocated; _thread_off_key_destructor; _thread_off_linkmap; _thread_off_next; _thread_off_report_events; _thread_off_state; _thread_off_tcb; _thread_off_tid; _thread_off_tlsindex; _thread_size_key; _thread_state_running; _thread_state_zoombie; - - __pthread_map_stacks_exec; }; FBSD_1.1 { __pthread_cleanup_pop_imp; __pthread_cleanup_push_imp; pthread_attr_getaffinity_np; pthread_attr_setaffinity_np; pthread_getaffinity_np; pthread_getcpuclockid; pthread_setaffinity_np; pthread_mutex_getspinloops_np; pthread_mutex_getyieldloops_np; pthread_mutex_isowned_np; pthread_mutex_setspinloops_np; pthread_mutex_setyieldloops_np; }; FBSD_1.2 { pthread_getthreadid_np; }; Index: projects/release-pkg/lib/libthr/thread/thr_private.h =================================================================== --- projects/release-pkg/lib/libthr/thread/thr_private.h (revision 295422) +++ projects/release-pkg/lib/libthr/thread/thr_private.h (revision 295423) @@ -1,939 +1,941 @@ /* * Copyright (C) 2005 Daniel M. Eischen * Copyright (c) 2005 David Xu * Copyright (c) 1995-1998 John Birrell . * * 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 unmodified, 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 ``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. * * $FreeBSD$ */ #ifndef _THR_PRIVATE_H #define _THR_PRIVATE_H /* * Include files. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SYM_FB10(sym) __CONCAT(sym, _fb10) #define SYM_FBP10(sym) __CONCAT(sym, _fbp10) #define WEAK_REF(sym, alias) __weak_reference(sym, alias) #define SYM_COMPAT(sym, impl, ver) __sym_compat(sym, impl, ver) #define SYM_DEFAULT(sym, impl, ver) __sym_default(sym, impl, ver) #define FB10_COMPAT(func, sym) \ WEAK_REF(func, SYM_FB10(sym)); \ SYM_COMPAT(sym, SYM_FB10(sym), FBSD_1.0) #define FB10_COMPAT_PRIVATE(func, sym) \ WEAK_REF(func, SYM_FBP10(sym)); \ SYM_DEFAULT(sym, SYM_FBP10(sym), FBSDprivate_1.0) #include "pthread_md.h" #include "thr_umtx.h" #include "thread_db.h" #ifdef _PTHREAD_FORCED_UNWIND #define _BSD_SOURCE #include #endif typedef TAILQ_HEAD(pthreadlist, pthread) pthreadlist; typedef TAILQ_HEAD(atfork_head, pthread_atfork) atfork_head; TAILQ_HEAD(mutex_queue, pthread_mutex); /* Signal to do cancellation */ #define SIGCANCEL SIGTHR /* * Kernel fatal error handler macro. */ #define PANIC(string) _thread_exit(__FILE__,__LINE__,string) /* Output debug messages like this: */ #define stdout_debug(args...) _thread_printf(STDOUT_FILENO, ##args) #define stderr_debug(args...) _thread_printf(STDERR_FILENO, ##args) #ifdef _PTHREADS_INVARIANTS #define THR_ASSERT(cond, msg) do { \ if (__predict_false(!(cond))) \ PANIC(msg); \ } while (0) #else #define THR_ASSERT(cond, msg) #endif #ifdef PIC # define STATIC_LIB_REQUIRE(name) #else # define STATIC_LIB_REQUIRE(name) __asm (".globl " #name) #endif #define TIMESPEC_ADD(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec + (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec + (val)->tv_nsec; \ if ((dst)->tv_nsec >= 1000000000) { \ (dst)->tv_sec++; \ (dst)->tv_nsec -= 1000000000; \ } \ } while (0) #define TIMESPEC_SUB(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ if ((dst)->tv_nsec < 0) { \ (dst)->tv_sec--; \ (dst)->tv_nsec += 1000000000; \ } \ } while (0) /* XXX These values should be same as those defined in pthread.h */ #define THR_MUTEX_INITIALIZER ((struct pthread_mutex *)NULL) #define THR_ADAPTIVE_MUTEX_INITIALIZER ((struct pthread_mutex *)1) #define THR_MUTEX_DESTROYED ((struct pthread_mutex *)2) #define THR_COND_INITIALIZER ((struct pthread_cond *)NULL) #define THR_COND_DESTROYED ((struct pthread_cond *)1) #define THR_RWLOCK_INITIALIZER ((struct pthread_rwlock *)NULL) #define THR_RWLOCK_DESTROYED ((struct pthread_rwlock *)1) #define PMUTEX_FLAG_TYPE_MASK 0x0ff #define PMUTEX_FLAG_PRIVATE 0x100 #define PMUTEX_FLAG_DEFERED 0x200 #define PMUTEX_TYPE(mtxflags) ((mtxflags) & PMUTEX_FLAG_TYPE_MASK) #define MAX_DEFER_WAITERS 50 struct pthread_mutex { /* * Lock for accesses to this structure. */ struct umutex m_lock; int m_flags; struct pthread *m_owner; int m_count; int m_spinloops; int m_yieldloops; /* * Link for all mutexes a thread currently owns. */ TAILQ_ENTRY(pthread_mutex) m_qe; }; struct pthread_mutex_attr { enum pthread_mutextype m_type; int m_protocol; int m_ceiling; }; #define PTHREAD_MUTEXATTR_STATIC_INITIALIZER \ { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, MUTEX_FLAGS_PRIVATE } struct pthread_cond { __uint32_t __has_user_waiters; __uint32_t __has_kern_waiters; __uint32_t __flags; __uint32_t __clock_id; }; struct pthread_cond_attr { int c_pshared; int c_clockid; }; struct pthread_barrier { struct umutex b_lock; struct ucond b_cv; int64_t b_cycle; int b_count; int b_waiters; int b_refcount; int b_destroying; }; struct pthread_barrierattr { int pshared; }; struct pthread_spinlock { struct umutex s_lock; }; /* * Flags for condition variables. */ #define COND_FLAGS_PRIVATE 0x01 #define COND_FLAGS_INITED 0x02 #define COND_FLAGS_BUSY 0x04 /* * Cleanup definitions. */ struct pthread_cleanup { struct pthread_cleanup *prev; void (*routine)(void *); void *routine_arg; int onheap; }; #define THR_CLEANUP_PUSH(td, func, arg) { \ struct pthread_cleanup __cup; \ \ __cup.routine = func; \ __cup.routine_arg = arg; \ __cup.onheap = 0; \ __cup.prev = (td)->cleanup; \ (td)->cleanup = &__cup; #define THR_CLEANUP_POP(td, exec) \ (td)->cleanup = __cup.prev; \ if ((exec) != 0) \ __cup.routine(__cup.routine_arg); \ } struct pthread_atfork { TAILQ_ENTRY(pthread_atfork) qe; void (*prepare)(void); void (*parent)(void); void (*child)(void); }; struct pthread_attr { #define pthread_attr_start_copy sched_policy int sched_policy; int sched_inherit; int prio; int suspend; #define THR_STACK_USER 0x100 /* 0xFF reserved for */ int flags; void *stackaddr_attr; size_t stacksize_attr; size_t guardsize_attr; #define pthread_attr_end_copy cpuset cpuset_t *cpuset; size_t cpusetsize; }; struct wake_addr { struct wake_addr *link; unsigned int value; char pad[12]; }; struct sleepqueue { TAILQ_HEAD(, pthread) sq_blocked; SLIST_HEAD(, sleepqueue) sq_freeq; LIST_ENTRY(sleepqueue) sq_hash; SLIST_ENTRY(sleepqueue) sq_flink; void *sq_wchan; int sq_type; }; /* * Thread creation state attributes. */ #define THR_CREATE_RUNNING 0 #define THR_CREATE_SUSPENDED 1 /* * Miscellaneous definitions. */ #define THR_STACK_DEFAULT (sizeof(void *) / 4 * 1024 * 1024) /* * Maximum size of initial thread's stack. This perhaps deserves to be larger * than the stacks of other threads, since many applications are likely to run * almost entirely on this stack. */ #define THR_STACK_INITIAL (THR_STACK_DEFAULT * 2) /* * Define priorities returned by kernel. */ #define THR_MIN_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_min) #define THR_MAX_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_max) #define THR_DEF_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_default) #define THR_MIN_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_min) #define THR_MAX_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_max) #define THR_DEF_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_default) /* XXX The SCHED_FIFO should have same priority range as SCHED_RR */ #define THR_MIN_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO_1].pri_min) #define THR_MAX_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO-1].pri_max) #define THR_DEF_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO-1].pri_default) struct pthread_prio { int pri_min; int pri_max; int pri_default; }; struct pthread_rwlockattr { int pshared; }; struct pthread_rwlock { struct urwlock lock; struct pthread *owner; }; /* * Thread states. */ enum pthread_state { PS_RUNNING, PS_DEAD }; struct pthread_specific_elem { const void *data; int seqno; }; struct pthread_key { volatile int allocated; int seqno; void (*destructor)(void *); }; /* * lwpid_t is 32bit but kernel thr API exports tid as long type * to preserve the ABI for M:N model in very early date (r131431). */ #define TID(thread) ((uint32_t) ((thread)->tid)) /* * Thread structure. */ struct pthread { #define _pthread_startzero tid /* Kernel thread id. */ long tid; #define TID_TERMINATED 1 /* * Lock for accesses to this thread structure. */ struct umutex lock; /* Internal condition variable cycle number. */ uint32_t cycle; /* How many low level locks the thread held. */ int locklevel; /* * Set to non-zero when this thread has entered a critical * region. We allow for recursive entries into critical regions. */ int critical_count; /* Signal blocked counter. */ int sigblock; /* Queue entry for list of all threads. */ TAILQ_ENTRY(pthread) tle; /* link for all threads in process */ /* Queue entry for GC lists. */ TAILQ_ENTRY(pthread) gcle; /* Hash queue entry. */ LIST_ENTRY(pthread) hle; /* Sleep queue entry */ TAILQ_ENTRY(pthread) wle; /* Threads reference count. */ int refcount; /* * Thread start routine, argument, stack pointer and thread * attributes. */ void *(*start_routine)(void *); void *arg; struct pthread_attr attr; #define SHOULD_CANCEL(thr) \ ((thr)->cancel_pending && (thr)->cancel_enable && \ (thr)->no_cancel == 0) /* Cancellation is enabled */ int cancel_enable; /* Cancellation request is pending */ int cancel_pending; /* Thread is at cancellation point */ int cancel_point; /* Cancellation is temporarily disabled */ int no_cancel; /* Asynchronouse cancellation is enabled */ int cancel_async; /* Cancellation is in progress */ int cancelling; /* Thread temporary signal mask. */ sigset_t sigmask; /* Thread should unblock SIGCANCEL. */ int unblock_sigcancel; /* In sigsuspend state */ int in_sigsuspend; /* deferred signal info */ siginfo_t deferred_siginfo; /* signal mask to restore. */ sigset_t deferred_sigmask; /* the sigaction should be used for deferred signal. */ struct sigaction deferred_sigact; /* deferred signal delivery is performed, do not reenter. */ int deferred_run; /* Force new thread to exit. */ int force_exit; /* Thread state: */ enum pthread_state state; /* * Error variable used instead of errno. The function __error() * returns a pointer to this. */ int error; /* * The joiner is the thread that is joining to this thread. The * join status keeps track of a join operation to another thread. */ struct pthread *joiner; /* Miscellaneous flags; only set with scheduling lock held. */ int flags; #define THR_FLAGS_PRIVATE 0x0001 #define THR_FLAGS_NEED_SUSPEND 0x0002 /* thread should be suspended */ #define THR_FLAGS_SUSPENDED 0x0004 /* thread is suspended */ #define THR_FLAGS_DETACHED 0x0008 /* thread is detached */ /* Thread list flags; only set with thread list lock held. */ int tlflags; #define TLFLAGS_GC_SAFE 0x0001 /* thread safe for cleaning */ #define TLFLAGS_IN_TDLIST 0x0002 /* thread in all thread list */ #define TLFLAGS_IN_GCLIST 0x0004 /* thread in gc list */ /* Queue of currently owned NORMAL or PRIO_INHERIT type mutexes. */ struct mutex_queue mutexq; /* Queue of all owned PRIO_PROTECT mutexes. */ struct mutex_queue pp_mutexq; void *ret; struct pthread_specific_elem *specific; int specific_data_count; /* Number rwlocks rdlocks held. */ int rdlock_count; /* * Current locks bitmap for rtld. */ int rtld_bits; /* Thread control block */ struct tcb *tcb; /* Cleanup handlers Link List */ struct pthread_cleanup *cleanup; #ifdef _PTHREAD_FORCED_UNWIND struct _Unwind_Exception ex; void *unwind_stackend; int unwind_disabled; #endif /* * Magic value to help recognize a valid thread structure * from an invalid one: */ #define THR_MAGIC ((u_int32_t) 0xd09ba115) u_int32_t magic; /* Enable event reporting */ int report_events; /* Event mask */ int event_mask; /* Event */ td_event_msg_t event_buf; /* Wait channel */ void *wchan; /* Referenced mutex. */ struct pthread_mutex *mutex_obj; /* Thread will sleep. */ int will_sleep; /* Number of threads deferred. */ int nwaiter_defer; /* Deferred threads from pthread_cond_signal. */ unsigned int *defer_waiters[MAX_DEFER_WAITERS]; #define _pthread_endzero wake_addr struct wake_addr *wake_addr; #define WAKE_ADDR(td) ((td)->wake_addr) /* Sleep queue */ struct sleepqueue *sleepqueue; }; #define THR_SHOULD_GC(thrd) \ ((thrd)->refcount == 0 && (thrd)->state == PS_DEAD && \ ((thrd)->flags & THR_FLAGS_DETACHED) != 0) #define THR_IN_CRITICAL(thrd) \ (((thrd)->locklevel > 0) || \ ((thrd)->critical_count > 0)) #define THR_CRITICAL_ENTER(thrd) \ (thrd)->critical_count++ #define THR_CRITICAL_LEAVE(thrd) \ do { \ (thrd)->critical_count--; \ _thr_ast(thrd); \ } while (0) #define THR_UMUTEX_TRYLOCK(thrd, lck) \ _thr_umutex_trylock((lck), TID(thrd)) #define THR_UMUTEX_LOCK(thrd, lck) \ _thr_umutex_lock((lck), TID(thrd)) #define THR_UMUTEX_TIMEDLOCK(thrd, lck, timo) \ _thr_umutex_timedlock((lck), TID(thrd), (timo)) #define THR_UMUTEX_UNLOCK(thrd, lck) \ _thr_umutex_unlock((lck), TID(thrd)) #define THR_LOCK_ACQUIRE(thrd, lck) \ do { \ (thrd)->locklevel++; \ _thr_umutex_lock(lck, TID(thrd)); \ } while (0) #define THR_LOCK_ACQUIRE_SPIN(thrd, lck) \ do { \ (thrd)->locklevel++; \ _thr_umutex_lock_spin(lck, TID(thrd)); \ } while (0) #ifdef _PTHREADS_INVARIANTS #define THR_ASSERT_LOCKLEVEL(thrd) \ do { \ if (__predict_false((thrd)->locklevel <= 0)) \ _thr_assert_lock_level(); \ } while (0) #else #define THR_ASSERT_LOCKLEVEL(thrd) #endif #define THR_LOCK_RELEASE(thrd, lck) \ do { \ THR_ASSERT_LOCKLEVEL(thrd); \ _thr_umutex_unlock((lck), TID(thrd)); \ (thrd)->locklevel--; \ _thr_ast(thrd); \ } while (0) #define THR_LOCK(curthrd) THR_LOCK_ACQUIRE(curthrd, &(curthrd)->lock) #define THR_UNLOCK(curthrd) THR_LOCK_RELEASE(curthrd, &(curthrd)->lock) #define THR_THREAD_LOCK(curthrd, thr) THR_LOCK_ACQUIRE(curthrd, &(thr)->lock) #define THR_THREAD_UNLOCK(curthrd, thr) THR_LOCK_RELEASE(curthrd, &(thr)->lock) #define THREAD_LIST_RDLOCK(curthrd) \ do { \ (curthrd)->locklevel++; \ _thr_rwl_rdlock(&_thr_list_lock); \ } while (0) #define THREAD_LIST_WRLOCK(curthrd) \ do { \ (curthrd)->locklevel++; \ _thr_rwl_wrlock(&_thr_list_lock); \ } while (0) #define THREAD_LIST_UNLOCK(curthrd) \ do { \ _thr_rwl_unlock(&_thr_list_lock); \ (curthrd)->locklevel--; \ _thr_ast(curthrd); \ } while (0) /* * Macros to insert/remove threads to the all thread list and * the gc list. */ #define THR_LIST_ADD(thrd) do { \ if (((thrd)->tlflags & TLFLAGS_IN_TDLIST) == 0) { \ TAILQ_INSERT_HEAD(&_thread_list, thrd, tle); \ _thr_hash_add(thrd); \ (thrd)->tlflags |= TLFLAGS_IN_TDLIST; \ } \ } while (0) #define THR_LIST_REMOVE(thrd) do { \ if (((thrd)->tlflags & TLFLAGS_IN_TDLIST) != 0) { \ TAILQ_REMOVE(&_thread_list, thrd, tle); \ _thr_hash_remove(thrd); \ (thrd)->tlflags &= ~TLFLAGS_IN_TDLIST; \ } \ } while (0) #define THR_GCLIST_ADD(thrd) do { \ if (((thrd)->tlflags & TLFLAGS_IN_GCLIST) == 0) { \ TAILQ_INSERT_HEAD(&_thread_gc_list, thrd, gcle);\ (thrd)->tlflags |= TLFLAGS_IN_GCLIST; \ _gc_count++; \ } \ } while (0) #define THR_GCLIST_REMOVE(thrd) do { \ if (((thrd)->tlflags & TLFLAGS_IN_GCLIST) != 0) { \ TAILQ_REMOVE(&_thread_gc_list, thrd, gcle); \ (thrd)->tlflags &= ~TLFLAGS_IN_GCLIST; \ _gc_count--; \ } \ } while (0) #define THR_REF_ADD(curthread, pthread) { \ THR_CRITICAL_ENTER(curthread); \ pthread->refcount++; \ } while (0) #define THR_REF_DEL(curthread, pthread) { \ pthread->refcount--; \ THR_CRITICAL_LEAVE(curthread); \ } while (0) #define GC_NEEDED() (_gc_count >= 5) #define SHOULD_REPORT_EVENT(curthr, e) \ (curthr->report_events && \ (((curthr)->event_mask | _thread_event_mask ) & e) != 0) extern int __isthreaded; /* * Global variables for the pthread kernel. */ extern char *_usrstack __hidden; extern struct pthread *_thr_initial __hidden; /* For debugger */ extern int _libthr_debug; extern int _thread_event_mask; extern struct pthread *_thread_last_event; /* List of all threads: */ extern pthreadlist _thread_list; /* List of threads needing GC: */ extern pthreadlist _thread_gc_list __hidden; extern int _thread_active_threads; extern atfork_head _thr_atfork_list __hidden; extern struct urwlock _thr_atfork_lock __hidden; /* Default thread attributes: */ extern struct pthread_attr _pthread_attr_default __hidden; /* Default mutex attributes: */ extern struct pthread_mutex_attr _pthread_mutexattr_default __hidden; extern struct pthread_mutex_attr _pthread_mutexattr_adaptive_default __hidden; /* Default condition variable attributes: */ extern struct pthread_cond_attr _pthread_condattr_default __hidden; extern struct pthread_prio _thr_priorities[] __hidden; extern pid_t _thr_pid __hidden; extern int _thr_is_smp __hidden; extern size_t _thr_guard_default __hidden; extern size_t _thr_stack_default __hidden; extern size_t _thr_stack_initial __hidden; extern int _thr_page_size __hidden; extern int _thr_spinloops __hidden; extern int _thr_yieldloops __hidden; extern int _thr_queuefifo __hidden; /* Garbage thread count. */ extern int _gc_count __hidden; extern struct umutex _mutex_static_lock __hidden; extern struct umutex _cond_static_lock __hidden; extern struct umutex _rwlock_static_lock __hidden; extern struct umutex _keytable_lock __hidden; extern struct urwlock _thr_list_lock __hidden; extern struct umutex _thr_event_lock __hidden; extern struct umutex _suspend_all_lock __hidden; extern int _suspend_all_waiters __hidden; extern int _suspend_all_cycle __hidden; extern struct pthread *_single_thread __hidden; /* * Function prototype definitions. */ __BEGIN_DECLS int _thr_setthreaded(int) __hidden; int _mutex_cv_lock(struct pthread_mutex *, int) __hidden; int _mutex_cv_unlock(struct pthread_mutex *, int *, int *) __hidden; int _mutex_cv_attach(struct pthread_mutex *, int) __hidden; int _mutex_cv_detach(struct pthread_mutex *, int *) __hidden; int _mutex_owned(struct pthread *, const struct pthread_mutex *) __hidden; int _mutex_reinit(pthread_mutex_t *) __hidden; void _mutex_fork(struct pthread *curthread) __hidden; void _libpthread_init(struct pthread *) __hidden; struct pthread *_thr_alloc(struct pthread *) __hidden; void _thread_exit(const char *, int, const char *) __hidden __dead2; int _thr_ref_add(struct pthread *, struct pthread *, int) __hidden; void _thr_ref_delete(struct pthread *, struct pthread *) __hidden; void _thr_ref_delete_unlocked(struct pthread *, struct pthread *) __hidden; int _thr_find_thread(struct pthread *, struct pthread *, int) __hidden; void _thr_rtld_init(void) __hidden; void _thr_rtld_postfork_child(void) __hidden; int _thr_stack_alloc(struct pthread_attr *) __hidden; void _thr_stack_free(struct pthread_attr *) __hidden; void _thr_free(struct pthread *, struct pthread *) __hidden; void _thr_gc(struct pthread *) __hidden; void _thread_cleanupspecific(void) __hidden; void _thread_printf(int, const char *, ...) __hidden; void _thr_spinlock_init(void) __hidden; void _thr_cancel_enter(struct pthread *) __hidden; void _thr_cancel_enter2(struct pthread *, int) __hidden; void _thr_cancel_leave(struct pthread *, int) __hidden; void _thr_testcancel(struct pthread *) __hidden; void _thr_signal_block(struct pthread *) __hidden; void _thr_signal_unblock(struct pthread *) __hidden; void _thr_signal_init(int) __hidden; void _thr_signal_deinit(void) __hidden; int _thr_send_sig(struct pthread *, int sig) __hidden; void _thr_list_init(void) __hidden; void _thr_hash_add(struct pthread *) __hidden; void _thr_hash_remove(struct pthread *) __hidden; struct pthread *_thr_hash_find(struct pthread *) __hidden; void _thr_link(struct pthread *, struct pthread *) __hidden; void _thr_unlink(struct pthread *, struct pthread *) __hidden; void _thr_assert_lock_level(void) __hidden __dead2; void _thr_ast(struct pthread *) __hidden; void _thr_once_init(void) __hidden; void _thr_report_creation(struct pthread *curthread, struct pthread *newthread) __hidden; void _thr_report_death(struct pthread *curthread) __hidden; int _thr_getscheduler(lwpid_t, int *, struct sched_param *) __hidden; int _thr_setscheduler(lwpid_t, int, const struct sched_param *) __hidden; void _thr_signal_prefork(void) __hidden; void _thr_signal_postfork(void) __hidden; void _thr_signal_postfork_child(void) __hidden; void _thr_suspend_all_lock(struct pthread *) __hidden; void _thr_suspend_all_unlock(struct pthread *) __hidden; void _thr_try_gc(struct pthread *, struct pthread *) __hidden; int _rtp_to_schedparam(const struct rtprio *rtp, int *policy, struct sched_param *param) __hidden; int _schedparam_to_rtp(int policy, const struct sched_param *param, struct rtprio *rtp) __hidden; void _thread_bp_create(void); void _thread_bp_death(void); int _sched_yield(void); void _pthread_cleanup_push(void (*)(void *), void *); void _pthread_cleanup_pop(int); void _pthread_exit_mask(void *status, sigset_t *mask) __dead2 __hidden; void _pthread_cancel_enter(int maycancel); void _pthread_cancel_leave(int maycancel); /* #include */ #ifdef _SYS_FCNTL_H_ int __sys_fcntl(int, int, ...); int __sys_openat(int, const char *, int, ...); #endif /* #include */ #ifdef _SIGNAL_H_ int __sys_kill(pid_t, int); int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigpending(sigset_t *); int __sys_sigprocmask(int, const sigset_t *, sigset_t *); int __sys_sigsuspend(const sigset_t *); int __sys_sigreturn(const ucontext_t *); int __sys_sigaltstack(const struct sigaltstack *, struct sigaltstack *); int __sys_sigwait(const sigset_t *, int *); int __sys_sigtimedwait(const sigset_t *, siginfo_t *, const struct timespec *); int __sys_sigwaitinfo(const sigset_t *set, siginfo_t *info); #endif /* #include */ #ifdef _TIME_H_ int __sys_nanosleep(const struct timespec *, struct timespec *); #endif /* #include */ #ifdef _SYS_UCONTEXT_H_ int __sys_setcontext(const ucontext_t *ucp); int __sys_swapcontext(ucontext_t *oucp, const ucontext_t *ucp); #endif /* #include */ #ifdef _UNISTD_H_ int __sys_close(int); int __sys_fork(void); pid_t __sys_getpid(void); ssize_t __sys_read(int, void *, size_t); void __sys_exit(int); #endif static inline int _thr_isthreaded(void) { return (__isthreaded != 0); } static inline int _thr_is_inited(void) { return (_thr_initial != NULL); } static inline void _thr_check_init(void) { if (_thr_initial == NULL) _libpthread_init(NULL); } struct wake_addr *_thr_alloc_wake_addr(void); void _thr_release_wake_addr(struct wake_addr *); int _thr_sleep(struct pthread *, int, const struct timespec *); void _thr_wake_addr_init(void) __hidden; static inline void _thr_clear_wake(struct pthread *td) { td->wake_addr->value = 0; } static inline int _thr_is_woken(struct pthread *td) { return td->wake_addr->value != 0; } static inline void _thr_set_wake(unsigned int *waddr) { *waddr = 1; _thr_umtx_wake(waddr, INT_MAX, 0); } void _thr_wake_all(unsigned int *waddrs[], int) __hidden; static inline struct pthread * _sleepq_first(struct sleepqueue *sq) { return TAILQ_FIRST(&sq->sq_blocked); } void _sleepq_init(void) __hidden; struct sleepqueue *_sleepq_alloc(void) __hidden; void _sleepq_free(struct sleepqueue *) __hidden; void _sleepq_lock(void *) __hidden; void _sleepq_unlock(void *) __hidden; struct sleepqueue *_sleepq_lookup(void *) __hidden; void _sleepq_add(void *, struct pthread *) __hidden; int _sleepq_remove(struct sleepqueue *, struct pthread *) __hidden; void _sleepq_drop(struct sleepqueue *, void (*cb)(struct pthread *, void *arg), void *) __hidden; int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, void *(calloc_cb)(size_t, size_t)); struct dl_phdr_info; void __pthread_cxa_finalize(struct dl_phdr_info *phdr_info); void _thr_tsd_unload(struct dl_phdr_info *phdr_info) __hidden; void _thr_sigact_unload(struct dl_phdr_info *phdr_info) __hidden; void _thr_stack_fix_protection(struct pthread *thrd); int *__error_threaded(void) __hidden; void __thr_interpose_libc(void) __hidden; pid_t __thr_fork(void); int __thr_setcontext(const ucontext_t *ucp); int __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) __hidden; int __thr_sigprocmask(int how, const sigset_t *set, sigset_t *oset); int __thr_sigsuspend(const sigset_t * set); int __thr_sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout); int __thr_sigwait(const sigset_t *set, int *sig); int __thr_sigwaitinfo(const sigset_t *set, siginfo_t *info); int __thr_swapcontext(ucontext_t *oucp, const ucontext_t *ucp); +void __thr_map_stacks_exec(void); + struct _spinlock; void __thr_spinunlock(struct _spinlock *lck); void __thr_spinlock(struct _spinlock *lck); struct tcb *_tcb_ctor(struct pthread *, int); void _tcb_dtor(struct tcb *); __END_DECLS #endif /* !_THR_PRIVATE_H */ Index: projects/release-pkg/lib/libthr/thread/thr_stack.c =================================================================== --- projects/release-pkg/lib/libthr/thread/thr_stack.c (revision 295422) +++ projects/release-pkg/lib/libthr/thread/thr_stack.c (revision 295423) @@ -1,318 +1,317 @@ /* * Copyright (c) 2001 Daniel Eischen * Copyright (c) 2000-2001 Jason Evans * 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 AUTHORS 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 AUTHORS 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 #include #include #include #include #include #include #include #include "thr_private.h" /* Spare thread stack. */ struct stack { LIST_ENTRY(stack) qe; /* Stack queue linkage. */ size_t stacksize; /* Stack size (rounded up). */ size_t guardsize; /* Guard size. */ void *stackaddr; /* Stack address. */ }; /* * Default sized (stack and guard) spare stack queue. Stacks are cached * to avoid additional complexity managing mmap()ed stack regions. Spare * stacks are used in LIFO order to increase cache locality. */ static LIST_HEAD(, stack) dstackq = LIST_HEAD_INITIALIZER(dstackq); /* * Miscellaneous sized (non-default stack and/or guard) spare stack queue. * Stacks are cached to avoid additional complexity managing mmap()ed * stack regions. This list is unordered, since ordering on both stack * size and guard size would be more trouble than it's worth. Stacks are * allocated from this cache on a first size match basis. */ static LIST_HEAD(, stack) mstackq = LIST_HEAD_INITIALIZER(mstackq); /** * Base address of the last stack allocated (including its red zone, if * there is one). Stacks are allocated contiguously, starting beyond the * top of the main stack. When a new stack is created, a red zone is * typically created (actually, the red zone is mapped with PROT_NONE) above * the top of the stack, such that the stack will not be able to grow all * the way to the bottom of the next stack. This isn't fool-proof. It is * possible for a stack to grow by a large amount, such that it grows into * the next stack, and as long as the memory within the red zone is never * accessed, nothing will prevent one thread stack from trouncing all over * the next. * * low memory * . . . . . . . . . . . . . . . . . . * | | * | stack 3 | start of 3rd thread stack * +-----------------------------------+ * | | * | Red Zone (guard page) | red zone for 2nd thread * | | * +-----------------------------------+ * | stack 2 - _thr_stack_default | top of 2nd thread stack * | | * | | * | | * | | * | stack 2 | * +-----------------------------------+ <-- start of 2nd thread stack * | | * | Red Zone | red zone for 1st thread * | | * +-----------------------------------+ * | stack 1 - _thr_stack_default | top of 1st thread stack * | | * | | * | | * | | * | stack 1 | * +-----------------------------------+ <-- start of 1st thread stack * | | (initial value of last_stack) * | Red Zone | * | | red zone for main thread * +-----------------------------------+ * | USRSTACK - _thr_stack_initial | top of main thread stack * | | ^ * | | | * | | | * | | | stack growth * | | * +-----------------------------------+ <-- start of main thread stack * (USRSTACK) * high memory * */ static char *last_stack = NULL; /* * Round size up to the nearest multiple of * _thr_page_size. */ static inline size_t round_up(size_t size) { if (size % _thr_page_size != 0) size = ((size / _thr_page_size) + 1) * _thr_page_size; return size; } void _thr_stack_fix_protection(struct pthread *thrd) { mprotect((char *)thrd->attr.stackaddr_attr + round_up(thrd->attr.guardsize_attr), round_up(thrd->attr.stacksize_attr), _rtld_get_stack_prot()); } static void singlethread_map_stacks_exec(void) { int mib[2]; struct rlimit rlim; u_long usrstack; size_t len; mib[0] = CTL_KERN; mib[1] = KERN_USRSTACK; len = sizeof(usrstack); if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &usrstack, &len, NULL, 0) == -1) return; if (getrlimit(RLIMIT_STACK, &rlim) == -1) return; mprotect((void *)(uintptr_t)(usrstack - rlim.rlim_cur), rlim.rlim_cur, _rtld_get_stack_prot()); } -void __pthread_map_stacks_exec(void); void -__pthread_map_stacks_exec(void) +__thr_map_stacks_exec(void) { struct pthread *curthread, *thrd; struct stack *st; if (!_thr_is_inited()) { singlethread_map_stacks_exec(); return; } curthread = _get_curthread(); THREAD_LIST_RDLOCK(curthread); LIST_FOREACH(st, &mstackq, qe) mprotect((char *)st->stackaddr + st->guardsize, st->stacksize, _rtld_get_stack_prot()); LIST_FOREACH(st, &dstackq, qe) mprotect((char *)st->stackaddr + st->guardsize, st->stacksize, _rtld_get_stack_prot()); TAILQ_FOREACH(thrd, &_thread_gc_list, gcle) _thr_stack_fix_protection(thrd); TAILQ_FOREACH(thrd, &_thread_list, tle) _thr_stack_fix_protection(thrd); THREAD_LIST_UNLOCK(curthread); } int _thr_stack_alloc(struct pthread_attr *attr) { struct pthread *curthread = _get_curthread(); struct stack *spare_stack; size_t stacksize; size_t guardsize; char *stackaddr; /* * Round up stack size to nearest multiple of _thr_page_size so * that mmap() * will work. If the stack size is not an even * multiple, we end up initializing things such that there is * unused space above the beginning of the stack, so the stack * sits snugly against its guard. */ stacksize = round_up(attr->stacksize_attr); guardsize = round_up(attr->guardsize_attr); attr->stackaddr_attr = NULL; attr->flags &= ~THR_STACK_USER; /* * Use the garbage collector lock for synchronization of the * spare stack lists and allocations from usrstack. */ THREAD_LIST_WRLOCK(curthread); /* * If the stack and guard sizes are default, try to allocate a stack * from the default-size stack cache: */ if ((stacksize == THR_STACK_DEFAULT) && (guardsize == _thr_guard_default)) { if ((spare_stack = LIST_FIRST(&dstackq)) != NULL) { /* Use the spare stack. */ LIST_REMOVE(spare_stack, qe); attr->stackaddr_attr = spare_stack->stackaddr; } } /* * The user specified a non-default stack and/or guard size, so try to * allocate a stack from the non-default size stack cache, using the * rounded up stack size (stack_size) in the search: */ else { LIST_FOREACH(spare_stack, &mstackq, qe) { if (spare_stack->stacksize == stacksize && spare_stack->guardsize == guardsize) { LIST_REMOVE(spare_stack, qe); attr->stackaddr_attr = spare_stack->stackaddr; break; } } } if (attr->stackaddr_attr != NULL) { /* A cached stack was found. Release the lock. */ THREAD_LIST_UNLOCK(curthread); } else { /* * Allocate a stack from or below usrstack, depending * on the LIBPTHREAD_BIGSTACK_MAIN env variable. */ if (last_stack == NULL) last_stack = _usrstack - _thr_stack_initial - _thr_guard_default; /* Allocate a new stack. */ stackaddr = last_stack - stacksize - guardsize; /* * Even if stack allocation fails, we don't want to try to * use this location again, so unconditionally decrement * last_stack. Under normal operating conditions, the most * likely reason for an mmap() error is a stack overflow of * the adjacent thread stack. */ last_stack -= (stacksize + guardsize); /* Release the lock before mmap'ing it. */ THREAD_LIST_UNLOCK(curthread); /* Map the stack and guard page together, and split guard page from allocated space: */ if ((stackaddr = mmap(stackaddr, stacksize + guardsize, _rtld_get_stack_prot(), MAP_STACK, -1, 0)) != MAP_FAILED && (guardsize == 0 || mprotect(stackaddr, guardsize, PROT_NONE) == 0)) { stackaddr += guardsize; } else { if (stackaddr != MAP_FAILED) munmap(stackaddr, stacksize + guardsize); stackaddr = NULL; } attr->stackaddr_attr = stackaddr; } if (attr->stackaddr_attr != NULL) return (0); else return (-1); } /* This function must be called with _thread_list_lock held. */ void _thr_stack_free(struct pthread_attr *attr) { struct stack *spare_stack; if ((attr != NULL) && ((attr->flags & THR_STACK_USER) == 0) && (attr->stackaddr_attr != NULL)) { spare_stack = (struct stack *) ((char *)attr->stackaddr_attr + attr->stacksize_attr - sizeof(struct stack)); spare_stack->stacksize = round_up(attr->stacksize_attr); spare_stack->guardsize = round_up(attr->guardsize_attr); spare_stack->stackaddr = attr->stackaddr_attr; if (spare_stack->stacksize == THR_STACK_DEFAULT && spare_stack->guardsize == _thr_guard_default) { /* Default stack/guard size. */ LIST_INSERT_HEAD(&dstackq, spare_stack, qe); } else { /* Non-default stack/guard size. */ LIST_INSERT_HEAD(&mstackq, spare_stack, qe); } attr->stackaddr_attr = NULL; } } Index: projects/release-pkg/lib/libthr/thread/thr_syscalls.c =================================================================== --- projects/release-pkg/lib/libthr/thread/thr_syscalls.c (revision 295422) +++ projects/release-pkg/lib/libthr/thread/thr_syscalls.c (revision 295423) @@ -1,659 +1,660 @@ /* * Copyright (c) 2014 The FreeBSD Foundation. * Copyright (C) 2005 David Xu . * Copyright (c) 2003 Daniel Eischen . * Copyright (C) 2000 Jason Evans . * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * 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(s), this list of conditions and the following disclaimer as * the first lines of this file unmodified other than the possible * addition of one or more copyright notices. * 2. Redistributions in binary form must reproduce the above copyright * notice(s), 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 COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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) 1995-1998 John Birrell * 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. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 "namespace.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 "un-namespace.h" #include "libc_private.h" #include "thr_private.h" static int __thr_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_accept(s, addr, addrlen); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * If thread is canceled, no socket is created. */ static int __thr_accept4(int s, struct sockaddr *addr, socklen_t *addrlen, int flags) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_accept4(s, addr, addrlen, flags); _thr_cancel_leave(curthread, ret == -1); return (ret); } static int __thr_aio_suspend(const struct aiocb * const iocbs[], int niocb, const struct timespec *timeout) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_aio_suspend(iocbs, niocb, timeout); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * According to manual of close(), the file descriptor is always deleted. * Here, thread is only canceled after the system call, so the file * descriptor is always deleted despite whether the thread is canceled * or not. */ static int __thr_close(int fd) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter2(curthread, 0); ret = __sys_close(fd); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * If the thread is canceled, connection is not made. */ static int __thr_connect(int fd, const struct sockaddr *name, socklen_t namelen) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_connect(fd, name, namelen); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * According to specification, only F_SETLKW is a cancellation point. * Thread is only canceled at start, or canceled if the system call * is failure, this means the function does not generate side effect * if it is canceled. */ static int __thr_fcntl(int fd, int cmd, ...) { struct pthread *curthread; int ret; va_list ap; curthread = _get_curthread(); va_start(ap, cmd); if (cmd == F_OSETLKW || cmd == F_SETLKW) { _thr_cancel_enter(curthread); ret = __sys_fcntl(fd, cmd, va_arg(ap, void *)); _thr_cancel_leave(curthread, ret == -1); } else { ret = __sys_fcntl(fd, cmd, va_arg(ap, void *)); } va_end(ap); return (ret); } /* * Cancellation behavior: * Thread may be canceled after system call. */ static int __thr_fsync(int fd) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter2(curthread, 0); ret = __sys_fsync(fd); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * Thread may be canceled after system call. */ static int __thr_msync(void *addr, size_t len, int flags) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter2(curthread, 0); ret = __sys_msync(addr, len, flags); _thr_cancel_leave(curthread, 1); return (ret); } static int __thr_nanosleep(const struct timespec *time_to_sleep, struct timespec *time_remaining) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_nanosleep(time_to_sleep, time_remaining); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * If the thread is canceled, file is not opened. */ static int __thr_openat(int fd, const char *path, int flags, ...) { struct pthread *curthread; int mode, ret; va_list ap; /* Check if the file is being created: */ if ((flags & O_CREAT) != 0) { /* Get the creation mode: */ va_start(ap, flags); mode = va_arg(ap, int); va_end(ap); } else { mode = 0; } curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_openat(fd, path, flags, mode); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns something, * the thread is not canceled. */ static int __thr_poll(struct pollfd *fds, unsigned int nfds, int timeout) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_poll(fds, nfds, timeout); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns something, * the thread is not canceled. */ static int __thr_ppoll(struct pollfd pfd[], nfds_t nfds, const struct timespec * timeout, const sigset_t *newsigmask) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_ppoll(pfd, nfds, timeout, newsigmask); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns something, * the thread is not canceled. */ static int __thr_pselect(int count, fd_set *rfds, fd_set *wfds, fd_set *efds, const struct timespec *timo, const sigset_t *mask) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_pselect(count, rfds, wfds, efds, timo, mask); _thr_cancel_leave(curthread, ret == -1); return (ret); } static int __thr_kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) { struct pthread *curthread; int ret; if (nevents == 0) { /* * No blocking, do not make the call cancellable. */ return (__sys_kevent(kq, changelist, nchanges, eventlist, nevents, timeout)); } curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_kevent(kq, changelist, nchanges, eventlist, nevents, timeout); _thr_cancel_leave(curthread, ret == -1 && nchanges == 0); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call got some data, * the thread is not canceled. */ static ssize_t __thr_read(int fd, void *buf, size_t nbytes) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_read(fd, buf, nbytes); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call got some data, * the thread is not canceled. */ static ssize_t __thr_readv(int fd, const struct iovec *iov, int iovcnt) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_readv(fd, iov, iovcnt); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call got some data, * the thread is not canceled. */ static ssize_t __thr_recvfrom(int s, void *b, size_t l, int f, struct sockaddr *from, socklen_t *fl) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_recvfrom(s, b, l, f, from, fl); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call got some data, * the thread is not canceled. */ static ssize_t __thr_recvmsg(int s, struct msghdr *m, int f) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_recvmsg(s, m, f); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns something, * the thread is not canceled. */ static int __thr_select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_select(numfds, readfds, writefds, exceptfds, timeout); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call sent * data, the thread is not canceled. */ static ssize_t __thr_sendmsg(int s, const struct msghdr *m, int f) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_sendmsg(s, m, f); _thr_cancel_leave(curthread, ret <= 0); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call sent some * data, the thread is not canceled. */ static ssize_t __thr_sendto(int s, const void *m, size_t l, int f, const struct sockaddr *t, socklen_t tl) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_sendto(s, m, l, f, t, tl); _thr_cancel_leave(curthread, ret <= 0); return (ret); } static int __thr_system(const char *string) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __libc_system(string); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * If thread is canceled, the system call is not completed, * this means not all bytes were drained. */ static int __thr_tcdrain(int fd) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __libc_tcdrain(fd); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns * a child pid, the thread is not canceled. */ static pid_t __thr_wait4(pid_t pid, int *status, int options, struct rusage *rusage) { struct pthread *curthread; pid_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_wait4(pid, status, options, rusage); _thr_cancel_leave(curthread, ret <= 0); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns * a child pid, the thread is not canceled. */ static pid_t __thr_wait6(idtype_t idtype, id_t id, int *status, int options, struct __wrusage *ru, siginfo_t *infop) { struct pthread *curthread; pid_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_wait6(idtype, id, status, options, ru, infop); _thr_cancel_leave(curthread, ret <= 0); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the thread wrote some data, * it is not canceled. */ static ssize_t __thr_write(int fd, const void *buf, size_t nbytes) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_write(fd, buf, nbytes); _thr_cancel_leave(curthread, (ret <= 0)); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the thread wrote some data, * it is not canceled. */ static ssize_t __thr_writev(int fd, const struct iovec *iov, int iovcnt) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_writev(fd, iov, iovcnt); _thr_cancel_leave(curthread, (ret <= 0)); return (ret); } void __thr_interpose_libc(void) { __set_error_selector(__error_threaded); #define SLOT(name) \ *(__libc_interposing_slot(INTERPOS_##name)) = \ (interpos_func_t)__thr_##name; SLOT(accept); SLOT(accept4); SLOT(aio_suspend); SLOT(close); SLOT(connect); SLOT(fcntl); SLOT(fsync); SLOT(fork); SLOT(msync); SLOT(nanosleep); SLOT(openat); SLOT(poll); SLOT(pselect); SLOT(read); SLOT(readv); SLOT(recvfrom); SLOT(recvmsg); SLOT(select); SLOT(sendmsg); SLOT(sendto); SLOT(setcontext); SLOT(sigaction); SLOT(sigprocmask); SLOT(sigsuspend); SLOT(sigwait); SLOT(sigtimedwait); SLOT(sigwaitinfo); SLOT(swapcontext); SLOT(system); SLOT(tcdrain); SLOT(wait4); SLOT(write); SLOT(writev); SLOT(spinlock); SLOT(spinunlock); SLOT(kevent); SLOT(wait6); SLOT(ppoll); + SLOT(map_stacks_exec); #undef SLOT *(__libc_interposing_slot( INTERPOS__pthread_mutex_init_calloc_cb)) = (interpos_func_t)_pthread_mutex_init_calloc_cb; } Index: projects/release-pkg/share/examples/jails/jib =================================================================== --- projects/release-pkg/share/examples/jails/jib (revision 295422) +++ projects/release-pkg/share/examples/jails/jib (revision 295423) @@ -1,367 +1,398 @@ #!/bin/sh #- # Copyright (c) 2016 Devin Teske # 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$ # ############################################################ IDENT(1) # # $Title: if_bridge(4) management script for vnet jails $ # ############################################################ INFORMATION # # Use this tool with jail.conf(5) (or rc.conf(5) ``legacy'' configuration) to -# manage `vnet' interfaces. In jail.conf(5) format: +# manage `vnet' interfaces for jails. Designed to automate the creation of vnet +# interface(s) during jail `prestart' and destroy said interface(s) during jail +# `poststop'. # +# In jail.conf(5) format: +# # ### BEGIN EXCERPT ### # # xxx { # host.hostname = "xxx.yyy"; # path = "/vm/xxx"; # # # # # NB: Below 2-lines required # # NB: The number of eNb_xxx interfaces should match the number of # # arguments given to `jib addm xxx' in exec.prestart value. # # # vnet; # vnet.interface = "e0b_xxx e1b_xxx ..."; # # exec.clean; # exec.system_user = "root"; # exec.jail_user = "root"; # # # # # NB: Below 2-lines required # # NB: The number of arguments after `jib addm xxx' should match # # the number of eNb_xxx arguments in vnet.interface value. # # # exec.prestart += "jib addm xxx em0 em1 ..."; # exec.poststop += "jib destroy xxx"; # # # Standard recipe # exec.start += "/bin/sh /etc/rc"; # exec.stop = "/bin/sh /etc/rc.shutdown"; # exec.consolelog = "/var/log/jail_xxx_console.log"; # mount.devfs; # # # Optional (default off) # #allow.mount; # #allow.set_hostname = 1; # #allow.sysvipc = 1; # #devfs_ruleset = "11"; # rule to unhide bpf for DHCP # } # # ### END EXCERPT ### # # In rc.conf(5) ``legacy'' format (used when /etc/jail.conf does not exist): # # ### BEGIN EXCERPT ### # # jail_enable="YES" # jail_list="xxx" # # # # # Global presets for all jails # # # jail_devfs_enable="YES" # mount devfs # # # # # Global options (default off) # # # #jail_mount_enable="YES" # mount /etc/fstab.{name} # #jail_set_hostname_allow="YES" # Allow hostname to change # #jail_sysvipc_allow="YES" # Allow SysV Interprocess Comm. # # # xxx # jail_xxx_hostname="xxx.shxd.cx" # hostname # jail_xxx_rootdir="/vm/xxx" # root directory # jail_xxx_vnet_interfaces="e0b_xxx e1bxxx ..." # vnet interface(s) # jail_xxx_exec_prestart0="jib addm xxx em0 em1 ..." # bridge interface(s) # jail_xxx_exec_poststop0="jib destroy xxx" # destroy interface(s) # #jail_xxx_mount_enable="YES" # mount /etc/fstab.xxx # #jail_xxx_devfs_ruleset="11" # rule to unhide bpf for DHCP # # ### END EXCERPT ### # # Note that the legacy rc.conf(5) format is converted to # /var/run/jail.{name}.conf by /etc/rc.d/jail if jail.conf(5) is missing. # # ASIDE: dhclient(8) inside a vnet jail... # # To allow dhclient(8) to work inside a vnet jail, make sure the following # appears in /etc/devfs.rules (which should be created if it doesn't exist): # # [devfsrules_jail=11] # add include $devfsrules_hide_all # add include $devfsrules_unhide_basic # add include $devfsrules_unhide_login # add include $devfsrules_unhide_bpf # # And set ether devfs.ruleset="11" (jail.conf(5)) or # jail_{name}_devfs_ruleset="11" (rc.conf(5)). # # NB: While this tool can't create every type of desirable topology, it should # handle most setups, minus some which considered exotic or purpose-built. # ############################################################ GLOBALS pgm="${0##*/}" # Program basename # # Global exit status # SUCCESS=0 FAILURE=1 ############################################################ FUNCTIONS usage() { local action usage descr exec >&2 echo "Usage: $pgm action [arguments]" echo "Actions:" for action in \ addm \ show \ show1 \ destroy \ ; do eval usage=\"\$jib_${action}_usage\" [ "$usage" ] || continue eval descr=\"\$jib_${action}_descr\" printf "\t%s\n\t\t%s\n" "$usage" "$descr" done exit $FAILURE } action_usage() { local usage action="$1" eval usage=\"\$jib_${action}_usage\" echo "Usage: $pgm $usage" >&2 exit $FAILURE } mustberoot_to_continue() { if [ "$( id -u )" -ne 0 ]; then echo "Must run as root!" >&2 exit $FAILURE fi } jib_addm_usage="addm [-b BRIDGE_NAME] NAME interface0 [interface1 ...]" jib_addm_descr="Creates e0b_NAME [e1b_NAME ...]" jib_addm() { local OPTIND=1 OPTARG flag bridge=bridge while getopts b: flag; do case "$flag" in b) bridge="${OPTARG:-bridge}" ;; *) action_usage addm # NOTREACHED esac done shift $(( $OPTIND - 1 )) local name="$1" [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -gt 1 ] || action_usage addm # NOTREACHED shift 1 # name mustberoot_to_continue local iface iface_devid eiface_devid_a eiface_devid_b local new num quad i=0 for iface in $*; do # 1. Make sure the interface doesn't exist already ifconfig "e${i}a_$name" > /dev/null 2>&1 && continue # 2. Bring the interface up ifconfig $iface up || return # 3. Make sure the interface has been bridged if ! ifconfig "$iface$bridge" > /dev/null 2>&1; then new=$( ifconfig bridge create ) || return ifconfig $new addm $iface || return ifconfig $new name "$iface$bridge" || return fi # 4. Create a new interface to the bridge new=$( ifconfig epair create ) || return ifconfig "$iface$bridge" addm $new || return # 5. Rename the new interface ifconfig $new name "e${i}a_$name" || return ifconfig ${new%a}b name "e${i}b_$name" || return # # 6. Set the MAC address of the new interface using a sensible # algorithm to prevent conflicts on the network. # - # The formula I'm using is ``SP:SS:SI:II:II:II'' where: - # + S denotes 16 bits of sum(1) data, split because P (below). + # The formula I'm using is ``NP:SS:SS:II:II:II'' where: + # + N denotes 4 bits used as a counter to support branching + # each parent interface up to 15 times under the same jail + # name (see S below). # + P denotes the special nibble whose value, if one of # 2, 6, A, or E (but usually 2) denotes a privately # administered MAC address (while remaining routable). + # + S denotes 16 bits, the sum(1) value of the jail name. # + I denotes bits that are inherited from parent interface. # # The S bits are a CRC-16 checksum of NAME, allowing the jail # to change the epair(4) generation order without affecting the - # MAC address. Meanwhile, if the jail NAME changes (e.g., it - # was duplicated and given a new name with no other changes), - # the underlying network interface changes, or the jail is - # moved to another host, the MAC address will be recalculated - # to a new, similarly unique value preventing conflict. + # MAC address. Meanwhile, if... + # + the jail NAME changes (e.g., it was duplicated and given + # a new name with no other changes) + # + the underlying network interface changes + # + the jail is moved to another host + # the MAC address will be recalculated to a new, similarly + # unique value preventing conflict. # iface_devid=$( ifconfig $iface ether | awk '/ether/,$0=$2' ) - eiface_devid_a=${iface_devid#??:??:?} - eiface_devid_b=${iface_devid#??:??:?} + eiface_devid_a=${iface_devid#??:??:??} + eiface_devid_b=${iface_devid#??:??:??} num=$( set -- `echo -n $name | sum` && echo $1 ) quad=$(( $num & 15 )) case "$quad" in 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; esac - eiface_devid_a=:$quad$eiface_devid_a - eiface_devid_b=:$quad$eiface_devid_b + eiface_devid_a=$quad$eiface_devid_a + eiface_devid_b=$quad$eiface_devid_b num=$(( $num >> 4 )) quad=$(( $num & 15 )) case "$quad" in 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; esac eiface_devid_a=$quad$eiface_devid_a eiface_devid_b=$quad$eiface_devid_b num=$(( $num >> 4 )) quad=$(( $num & 15 )) case "$quad" in 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; esac - eiface_devid_a=2:$quad$eiface_devid_a - eiface_devid_b=6:$quad$eiface_devid_b + eiface_devid_a=$quad:$eiface_devid_a + eiface_devid_b=$quad:$eiface_devid_b num=$(( $num >> 4 )) quad=$(( $num & 15 )) case "$quad" in 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; esac + case "$iface_devid" in + ?2:*|?6:*) + eiface_devid_a=a:$quad$eiface_devid_a + eiface_devid_b=e:$quad$eiface_devid_b + ;; + *) + eiface_devid_a=2:$quad$eiface_devid_a + eiface_devid_b=6:$quad$eiface_devid_b + esac + eval num=\$_${iface}_num + if [ "$num" ]; then + num=$(( $num + 1 )) + eval _${iface}_num=$num + else + num=0 + local _${iface}_num=$num + fi + quad=$(( $num & 15 )) + case "$quad" in + 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; + 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; + esac eiface_devid_a=$quad$eiface_devid_a eiface_devid_b=$quad$eiface_devid_b ifconfig "e${i}a_$name" ether $eiface_devid_a > /dev/null 2>&1 ifconfig "e${i}b_$name" ether $eiface_devid_b > /dev/null 2>&1 - i=$(( $i + 1 )) # on to next ng{i}_name + i=$(( $i + 1 )) # on to next e{i}b_name done # for iface } jib_show_usage="show" jib_show_descr="List possible NAME values for \`show NAME'" jib_show1_usage="show NAME" -jib_show1_descr="Lists ng0_NAME [ng1_NAME ...]" +jib_show1_descr="Lists e0b_NAME [e1b_NAME ...]" jib_show2_usage="show [NAME]" jib_show() { local OPTIND=1 OPTARG flag while getopts "" flag; do case "$flag" in *) action_usage show2 # NOTREACHED esac done shift $(( $OPTIND - 1 )) if [ $# -eq 0 ]; then ifconfig | awk ' /^[^:[:space:]]+:/ { iface = $1 sub(/:.*/, "", iface) next } $1 == "groups:" { for (n = split($0, group); n > 1; n--) { if (group[n] != "bridge") continue print iface next } }' | xargs -rn1 ifconfig | awk '$1 == "member:" && sub(/^e[[:digit:]]+a_/, "", $2), $0 = $2' | sort -u return fi ifconfig | awk -v name="$1" ' match($0, /^e[[:digit:]]+a_/) && sub(/:.*/, "") && substr($1, RSTART + RLENGTH) == name ' | sort } jib_destroy_usage="destroy NAME" jib_destroy_descr="Destroy e0b_NAME [e1b_NAME ...]" jib_destroy() { local OPTIND=1 OPTARG flag while getopts "" flag; do case "$flag" in *) action_usage destroy # NOTREACHED esac done shift $(( $OPTIND -1 )) local name="$1" [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] || action_usage destroy # NOTREACHED mustberoot_to_continue jib_show "$name" | xargs -rn1 -I eiface ifconfig eiface destroy } ############################################################ MAIN # # Command-line arguments # action="$1" [ "$action" ] || usage # NOTREACHED # # Validate action argument # if [ "$BASH_VERSION" ]; then type="$( type -t "jib_$action" )" || usage # NOTREACHED else type="$( type "jib_$action" 2> /dev/null )" || usage # NOTREACHED fi case "$type" in *function) shift 1 # action eval "jib_$action" \"\$@\" ;; *) usage # NOTREACHED esac ################################################################################ # END ################################################################################ Index: projects/release-pkg/share/examples/jails/jng =================================================================== --- projects/release-pkg/share/examples/jails/jng (revision 295422) +++ projects/release-pkg/share/examples/jails/jng (revision 295423) @@ -1,416 +1,442 @@ #!/bin/sh #- # Copyright (c) 2016 Devin Teske # 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$ # ############################################################ IDENT(1) # # $Title: netgraph(4) management script for vnet jails $ # ############################################################ INFORMATION # # Use this tool with jail.conf(5) (or rc.conf(5) ``legacy'' configuration) to -# manage `vnet' interfaces. In jail.conf(5) format: +# manage `vnet' interfaces for jails. Designed to automate the creation of vnet +# interface(s) during jail `prestart' and destroy said interface(s) during jail +# `poststop'. # +# In jail.conf(5) format: +# # ### BEGIN EXCERPT ### # # xxx { # host.hostname = "xxx.yyy"; # path = "/vm/xxx"; # # # # # NB: Below 2-lines required # # NB: The number of ngN_xxx interfaces should match the number of # # arguments given to `jng bridge xxx' in exec.prestart value. # # # vnet; # vnet.interface = "ng0_xxx ng1_xxx ..."; # # exec.clean; # exec.system_user = "root"; # exec.jail_user = "root"; # # # # # NB: Below 2-lines required # # NB: The number of arguments after `jng bridge xxx' should match # # the number of ngN_xxx arguments in vnet.interface value. # # # exec.prestart += "jng bridge xxx em0 em1 ..."; # exec.poststop += "jng shutdown xxx"; # # # Standard recipe # exec.start += "/bin/sh /etc/rc"; # exec.stop = "/bin/sh /etc/rc.shutdown"; # exec.consolelog = "/var/log/jail_xxx_console.log"; # mount.devfs; # # # Optional (default off) # #allow.mount; # #allow.set_hostname = 1; # #allow.sysvipc = 1; # #devfs_ruleset = "11"; # rule to unhide bpf for DHCP # } # # ### END EXCERPT ### # # In rc.conf(5) ``legacy'' format (used when /etc/jail.conf does not exist): # # ### BEGIN EXCERPT ### # # jail_enable="YES" # jail_list="xxx" # # # # # Global presets for all jails # # # jail_devfs_enable="YES" # mount devfs # # # # # Global options (default off) # # # #jail_mount_enable="YES" # mount /etc/fstab.{name} # #jail_set_hostname_allow="YES" # Allow hostname to change # #jail_sysvipc_allow="YES" # Allow SysV Interprocess Comm. # # # xxx # jail_xxx_hostname="xxx.shxd.cx" # hostname # jail_xxx_rootdir="/vm/xxx" # root directory # jail_xxx_vnet_interfaces="ng0_xxx ng1xxx ..." # vnet interface(s) # jail_xxx_exec_prestart0="jng bridge xxx em0 em1 ..." # bridge interface(s) # jail_xxx_exec_poststop0="jng shutdown xxx" # destroy interface(s) # #jail_xxx_mount_enable="YES" # mount /etc/fstab.xxx # #jail_xxx_devfs_ruleset="11" # rule to unhide bpf for DHCP # # ### END EXCERPT ### # # Note that the legacy rc.conf(5) format is converted to # /var/run/jail.{name}.conf by /etc/rc.d/jail if jail.conf(5) is missing. # # ASIDE: dhclient(8) inside a vnet jail... # # To allow dhclient(8) to work inside a vnet jail, make sure the following # appears in /etc/devfs.rules (which should be created if it doesn't exist): # # [devfsrules_jail=11] # add include $devfsrules_hide_all # add include $devfsrules_unhide_basic # add include $devfsrules_unhide_login # add include $devfsrules_unhide_bpf # # And set ether devfs.ruleset="11" (jail.conf(5)) or # jail_{name}_devfs_ruleset="11" (rc.conf(5)). # # NB: While this tool can't create every type of desirable topology, it should # handle most setups, minus some which considered exotic or purpose-built. # ############################################################ GLOBALS pgm="${0##*/}" # Program basename # # Global exit status # SUCCESS=0 FAILURE=1 ############################################################ FUNCTIONS usage() { local action usage descr exec >&2 echo "Usage: $pgm action [arguments]" echo "Actions:" for action in \ bridge \ graph \ show \ show1 \ shutdown \ ; do eval usage=\"\$jng_${action}_usage\" [ "$usage" ] || continue eval descr=\"\$jng_${action}_descr\" printf "\t%s\n\t\t%s\n" "$usage" "$descr" done exit $FAILURE } action_usage() { local usage action="$1" eval usage=\"\$jng_${action}_usage\" echo "Usage: $pgm $usage" >&2 exit $FAILURE } mustberoot_to_continue() { if [ "$( id -u )" -ne 0 ]; then echo "Must run as root!" >&2 exit $FAILURE fi } jng_bridge_usage="bridge [-b BRIDGE_NAME] NAME interface0 [interface1 ...]" jng_bridge_descr="Create ng0_NAME [ng1_NAME ...]" jng_bridge() { local OPTIND=1 OPTARG flag bridge=bridge while getopts b: flag; do case "$flag" in b) bridge="$OPTARG" [ "$bridge" ] || action_usage bridge ;; # NOTREACHED *) action_usage bridge # NOTREACHED esac done shift $(( $OPTIND - 1 )) local name="$1" [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -gt 1 ] || action_usage bridge # NOTREACHED shift 1 # name mustberoot_to_continue local iface iface_devid eiface eiface_devid local new num quad i=0 for iface in $*; do # 0. Make sure the interface doesn't exist already eiface=ng${i}_$name ngctl msg "$eiface:" getifname > /dev/null 2>&1 && continue # 1. Bring the interface up ifconfig $iface up || return # 2. Set promiscuous mode and don't overwrite src addr ngctl msg $iface: setpromisc 1 || return ngctl msg $iface: setautosrc 0 || return # 3. Make sure the interface has been bridged if ! ngctl info ${iface}bridge: > /dev/null 2>&1; then ngctl mkpeer $iface: bridge lower link0 || return ngctl connect $iface: $iface:lower upper link1 || return ngctl name $iface:lower ${iface}bridge || return fi # 3.5. Optionally create a secondary bridge if [ "$bridge" != "bridge" ] && ! ngctl info "$iface$bridge:" > /dev/null 2>&1 then num=2 while ngctl msg ${iface}bridge: getstats $num \ > /dev/null 2>&1 do num=$(( $num + 1 )) done ngctl mkpeer $iface:lower bridge link$num link1 || return ngctl name ${iface}bridge:link$num "$iface$bridge" || return fi # 4. Create a new interface to the bridge num=2 while ngctl msg "$iface$bridge:" getstats $num > /dev/null 2>&1 do num=$(( $num + 1 )) done ngctl mkpeer "$iface$bridge:" eiface link$num ether || return # 5. Rename the new interface while [ ${#eiface} -gt 15 ]; do # OS limitation eiface=${eiface%?} done new=$( set -- `ngctl show -n "$iface$bridge:link$num"` && echo $2 ) || return ngctl name "$iface$bridge:link$num" $eiface || return ifconfig $new name $eiface || return # # 6. Set the MAC address of the new interface using a sensible # algorithm to prevent conflicts on the network. # - # The formula I'm using is ``SP:SS:SI:II:II:II'' where: - # + S denotes 16 bits of sum(1) data, split because P (below). + # The formula I'm using is ``NP:SS:SS:II:II:II'' where: + # + N denotes 4 bits used as a counter to support branching + # each parent interface up to 15 times under the same jail + # name (see S below). # + P denotes the special nibble whose value, if one of # 2, 6, A, or E (but usually 2) denotes a privately # administered MAC address (while remaining routable). + # + S denotes 16 bits, the sum(1) value of the jail name. # + I denotes bits that are inherited from parent interface. # # The S bits are a CRC-16 checksum of NAME, allowing the jail # to change link numbers in ng_bridge(4) without affecting the - # MAC address. Meanwhile, if the jail NAME changes (e.g., it - # was duplicated and given a new name with no other changes), - # the underlying network interface changes, or the jail is - # moved to another host, the MAC address will be recalculated - # to a new, similarly unique value preventing conflict. + # MAC address. Meanwhile, if... + # + the jail NAME changes (e.g., it was duplicated and given + # a new name with no other changes) + # + the underlying network interface changes + # + the jail is moved to another host + # the MAC address will be recalculated to a new, similarly + # unique value preventing conflict. # iface_devid=$( ifconfig $iface ether | awk '/ether/,$0=$2' ) - eiface_devid=${iface_devid#??:??:?} + eiface_devid=${iface_devid#??:??:??} num=$( set -- `echo -n $name | sum` && echo $1 ) quad=$(( $num & 15 )) case "$quad" in 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; esac - eiface_devid=:$quad$eiface_devid + eiface_devid=$quad$eiface_devid num=$(( $num >> 4 )) quad=$(( $num & 15 )) case "$quad" in 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; esac eiface_devid=$quad$eiface_devid num=$(( $num >> 4 )) quad=$(( $num & 15 )) case "$quad" in 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; esac - eiface_devid=2:$quad$eiface_devid + eiface_devid=$quad:$eiface_devid num=$(( $num >> 4 )) + quad=$(( $num & 15 )) + case "$quad" in + 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; + 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; + esac + case "$iface_devid" in + ?2:*) eiface_devid=a:$quad$eiface_devid ;; + *) eiface_devid=2:$quad$eiface_devid + esac + eval num=\$_${iface}_num + if [ "$num" ]; then + num=$(( $num + 1 )) + eval _${iface}_num=$num + else + num=0 + local _${iface}_num=$num + fi quad=$(( $num & 15 )) case "$quad" in 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; esac eiface_devid=$quad$eiface_devid ifconfig $eiface ether $eiface_devid > /dev/null 2>&1 i=$(( $i + 1 )) # on to next ng{i}_name done # for iface } jng_graph_usage="graph [-f] [-T type] [-o output]" jng_graph_descr="Generate network graph (default output is \`jng.svg')" jng_graph() { local OPTIND=1 OPTARG flag local output=jng.svg output_type= force= while getopts fo:T: flag; do case "$flag" in f) force=1 ;; o) output="$OPTARG" ;; T) output_type="$OPTARG" ;; *) action_usage graph # NOTREACHED esac done shift $(( $OPTIND - 1 )) [ $# -eq 0 -a "$output" ] || action_usage graph # NOTREACHED mustberoot_to_continue if [ -e "$output" -a ! "$force" ]; then echo "$output: Already exists (use \`-f' to overwrite)" >&2 return $FAILURE fi if [ ! "$output_type" ]; then local valid suffix valid=$( dot -Txxx 2>&1 ) for suffix in ${valid##*:}; do [ "$output" != "${output%.$suffix}" ] || continue output_type=$suffix break done fi ngctl dot | dot ${output_type:+-T "$output_type"} -o "$output" } jng_show_usage="show" jng_show_descr="List possible NAME values for \`show NAME'" jng_show1_usage="show NAME" jng_show1_descr="Lists ng0_NAME [ng1_NAME ...]" jng_show2_usage="show [NAME]" jng_show() { local OPTIND=1 OPTARG flag while getopts "" flag; do case "$flag" in *) action_usage show2 # NOTREACHED esac done shift $(( $OPTIND - 1 )) mustberoot_to_continue if [ $# -eq 0 ]; then ngctl ls | awk '$4=="bridge",$0=$2' | xargs -rn1 -Ibridge ngctl show bridge: | awk 'sub(/^ng[[:digit:]]+_/, "", $2), $0 = $2' | sort -u return fi ngctl ls | awk -v name="$1" ' match($2, /^ng[[:digit:]]+_/) && substr($2, RSTART + RLENGTH) == name && $4 == "eiface", $0 = $2 ' | sort } jng_shutdown_usage="shutdown NAME" jng_shutdown_descr="Shutdown ng0_NAME [ng1_NAME ...]" jng_shutdown() { local OPTIND=1 OPTARG flag while getopts "" flag; do case "$flag" in *) action_usage shutdown # NOTREACHED esac done shift $(( $OPTIND -1 )) local name="$1" [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] || action_usage shutdown # NOTREACHED mustberoot_to_continue jng_show "$name" | xargs -rn1 -I eiface ngctl shutdown eiface: } ############################################################ MAIN # # Command-line arguments # action="$1" [ "$action" ] || usage # NOTREACHED # # Validate action argument # if [ "$BASH_VERSION" ]; then type="$( type -t "jng_$action" )" || usage # NOTREACHED else type="$( type "jng_$action" 2> /dev/null )" || usage # NOTREACHED fi case "$type" in *function) shift 1 # action eval "jng_$action" \"\$@\" ;; *) usage # NOTREACHED esac ################################################################################ # END ################################################################################ Index: projects/release-pkg/share =================================================================== --- projects/release-pkg/share (revision 295422) +++ projects/release-pkg/share (revision 295423) Property changes on: projects/release-pkg/share ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share:r295394-295422 Index: projects/release-pkg/sys/boot/Makefile.inc =================================================================== --- projects/release-pkg/sys/boot/Makefile.inc (revision 295422) +++ projects/release-pkg/sys/boot/Makefile.inc (revision 295423) @@ -1,3 +1,11 @@ # $FreeBSD$ SSP_CFLAGS= + +.if ${MACHINE_CPUARCH} == "arm" +# Do not generate movt/movw, because the relocation fixup for them does not +# translate to the -Bsymbolic -pie format required by self_reloc() in loader(8). +# Also, the fpu is not available in a standalone environment. +CFLAGS.clang+= -mllvm -arm-use-movt=0 +CFLAGS.clang+= -mfpu=none +.endif Index: projects/release-pkg/sys/boot/efi/loader/arch/arm/ldscript.arm =================================================================== --- projects/release-pkg/sys/boot/efi/loader/arch/arm/ldscript.arm (revision 295422) +++ projects/release-pkg/sys/boot/efi/loader/arch/arm/ldscript.arm (revision 295423) @@ -1,60 +1,62 @@ /* $FreeBSD$ */ OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { /* Read-only sections, merged into text segment: */ . = 0; ImageBase = .; .text : { *(.peheader) *(.text .stub .text.* .gnu.linkonce.t.*) /* .gnu.warning sections are handled specially by elf32.em. */ *(.gnu.warning) *(.gnu.linkonce.t*) } =0 _etext = .; PROVIDE (etext = .); - . = ALIGN(4096); + . = ALIGN(16); .data : { *(.data *.data.*) *(.gnu.linkonce.d*) *(.rodata) *(.rodata.*) CONSTRUCTORS + . = ALIGN(4); PROVIDE (__bss_start = .); *(.sbss) *(.scommon) *(.dynsbss) *(.dynbss) *(.bss) *(COMMON) + . = ALIGN(4); PROVIDE (__bss_end = .); } /* We want the small data sections together, so single-instruction offsets can access them all, and initialized data all before uninitialized, so we can shorten the on-disk segment size. */ .sdata : { *(.got.plt .got) *(.sdata*.sdata.* .gnu.linkonce.s.*) } set_Xcommand_set : { __start_set_Xcommand_set = .; *(set_Xcommand_set) __stop_set_Xcommand_set = .; } __gp = .; .plt : { *(.plt) } .dynamic : { *(.dynamic) } .reloc : { *(.reloc) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } .rel.dyn : { *(.rel.*) *(.relset_*) } _edata = .; .hash : { *(.hash) } } Index: projects/release-pkg/sys/boot/efi/loader/main.c =================================================================== --- projects/release-pkg/sys/boot/efi/loader/main.c (revision 295422) +++ projects/release-pkg/sys/boot/efi/loader/main.c (revision 295423) @@ -1,663 +1,753 @@ /*- * Copyright (c) 2008-2010 Rui Paulo * Copyright (c) 2006 Marcel Moolenaar * 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 ``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 #include #include #include #include #include #include #include #include #include #ifdef EFI_ZFS_BOOT #include #endif #include "loader_efi.h" extern char bootprog_name[]; extern char bootprog_rev[]; extern char bootprog_date[]; extern char bootprog_maker[]; struct arch_switch archsw; /* MI/MD interface boundary */ EFI_GUID acpi = ACPI_TABLE_GUID; EFI_GUID acpi20 = ACPI_20_TABLE_GUID; EFI_GUID devid = DEVICE_PATH_PROTOCOL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID mps = MPS_TABLE_GUID; EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; EFI_GUID hoblist = HOB_LIST_TABLE_GUID; EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; +EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void); #endif /* * Need this because EFI uses UTF-16 unicode string constants, but we * use UTF-8. We can't use printf due to the possiblity of \0 and we * don't support support wide characters either. */ static void print_str16(const CHAR16 *str) { int i; for (i = 0; str[i]; i++) printf("%c", (char)str[i]); } static void cp16to8(const CHAR16 *src, char *dst, size_t len) { size_t i; for (i = 0; i < len && src[i]; i++) dst[i] = (char)src[i]; } +static int +has_keyboard(void) +{ + EFI_STATUS status; + EFI_DEVICE_PATH *path; + EFI_HANDLE *hin, *hin_end, *walker; + UINTN sz; + int retval = 0; + + /* + * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and + * do the typical dance to get the right sized buffer. + */ + sz = 0; + hin = NULL; + status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); + if (status == EFI_BUFFER_TOO_SMALL) { + hin = (EFI_HANDLE *)malloc(sz); + status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, + hin); + if (EFI_ERROR(status)) + free(hin); + } + if (EFI_ERROR(status)) + return retval; + + /* + * Look at each of the handles. If it supports the device path protocol, + * use it to get the device path for this handle. Then see if that + * device path matches either the USB device path for keyboards or the + * legacy device path for keyboards. + */ + hin_end = &hin[sz / sizeof(*hin)]; + for (walker = hin; walker < hin_end; walker++) { + status = BS->HandleProtocol(*walker, &devid, (VOID **)&path); + if (EFI_ERROR(status)) + continue; + + while (!IsDevicePathEnd(path)) { + /* + * Check for the ACPI keyboard node. All PNP3xx nodes + * are keyboards of different flavors. Note: It is + * unclear of there's always a keyboard node when + * there's a keyboard controller, or if there's only one + * when a keyboard is detected at boot. + */ + if (DevicePathType(path) == ACPI_DEVICE_PATH && + (DevicePathSubType(path) == ACPI_DP || + DevicePathSubType(path) == ACPI_EXTENDED_DP)) { + ACPI_HID_DEVICE_PATH *acpi; + + acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; + if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && + (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { + retval = 1; + goto out; + } + /* + * Check for USB keyboard node, if present. Unlike a + * PS/2 keyboard, these definitely only appear when + * connected to the system. + */ + } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && + DevicePathSubType(path) == MSG_USB_CLASS_DP) { + USB_CLASS_DEVICE_PATH *usb; + + usb = (USB_CLASS_DEVICE_PATH *)(void *)path; + if (usb->DeviceClass == 3 && /* HID */ + usb->DeviceSubClass == 1 && /* Boot devices */ + usb->DeviceProtocol == 1) { /* Boot keyboards */ + retval = 1; + goto out; + } + } + path = NextDevicePathNode(path); + } + } +out: + free(hin); + return retval; +} + EFI_STATUS main(int argc, CHAR16 *argv[]) { char var[128]; EFI_LOADED_IMAGE *img; EFI_GUID *guid; int i, j, vargood, unit, howto; struct devsw *dev; uint64_t pool_guid; UINTN k; + int has_kbd; archsw.arch_autoload = efi_autoload; archsw.arch_getdev = efi_getdev; archsw.arch_copyin = efi_copyin; archsw.arch_copyout = efi_copyout; archsw.arch_readin = efi_readin; #ifdef EFI_ZFS_BOOT /* Note this needs to be set before ZFS init. */ archsw.arch_zfs_probe = efi_zfs_probe; #endif + has_kbd = has_keyboard(); + /* * XXX Chicken-and-egg problem; we want to have console output * early, but some console attributes may depend on reading from * eg. the boot device, which we can't do yet. We can use * printf() etc. once this is done. */ cons_probe(); /* * Parse the args to set the console settings, etc * boot1.efi passes these in, if it can read /boot.config or /boot/config * or iPXE may be setup to pass these in. * * Loop through the args, and for each one that contains an '=' that is * not the first character, add it to the environment. This allows * loader and kernel env vars to be passed on the command line. Convert * args from UCS-2 to ASCII (16 to 8 bit) as they are copied. */ howto = 0; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (j = 1; argv[i][j] != 0; j++) { int ch; ch = argv[i][j]; switch (ch) { case 'a': howto |= RB_ASKNAME; break; case 'd': howto |= RB_KDB; break; case 'D': howto |= RB_MULTIPLE; break; - case 'm': - howto |= RB_MUTE; - break; case 'h': howto |= RB_SERIAL; break; + case 'm': + howto |= RB_MUTE; + break; case 'p': howto |= RB_PAUSE; + break; + case 'P': + if (!has_kbd) + howto |= RB_SERIAL | RB_MULTIPLE; break; case 'r': howto |= RB_DFLTROOT; break; case 's': howto |= RB_SINGLE; break; case 'S': if (argv[i][j + 1] == 0) { if (i + 1 == argc) { setenv("comconsole_speed", "115200", 1); } else { cp16to8(&argv[i + 1][0], var, sizeof(var)); setenv("comconsole_speedspeed", var, 1); } i++; break; } else { cp16to8(&argv[i][j + 1], var, sizeof(var)); setenv("comconsole_speed", var, 1); break; } case 'v': howto |= RB_VERBOSE; break; } } } else { vargood = 0; for (j = 0; argv[i][j] != 0; j++) { if (j == sizeof(var)) { vargood = 0; break; } if (j > 0 && argv[i][j] == '=') vargood = 1; var[j] = (char)argv[i][j]; } if (vargood) { var[j] = 0; putenv(var); } } } for (i = 0; howto_names[i].ev != NULL; i++) if (howto & howto_names[i].mask) setenv(howto_names[i].ev, "YES", 1); if (howto & RB_MULTIPLE) { if (howto & RB_SERIAL) setenv("console", "comconsole efi" , 1); else setenv("console", "efi comconsole" , 1); } else if (howto & RB_SERIAL) { setenv("console", "comconsole" , 1); } if (efi_copy_init()) { printf("failed to allocate staging area\n"); return (EFI_BUFFER_TOO_SMALL); } /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); /* Get our loaded image protocol interface structure. */ BS->HandleProtocol(IH, &imgid, (VOID**)&img); printf("Command line arguments:"); for (i = 0; i < argc; i++) { printf(" "); print_str16(argv[i]); } printf("\n"); printf("Image base: 0x%lx\n", (u_long)img->ImageBase); printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); printf("EFI Firmware: "); /* printf doesn't understand EFI Unicode */ ST->ConOut->OutputString(ST->ConOut, ST->FirmwareVendor); printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); printf("\n"); printf("%s, Revision %s\n", bootprog_name, bootprog_rev); printf("(%s, %s)\n", bootprog_maker, bootprog_date); /* * Disable the watchdog timer. By default the boot manager sets * the timer to 5 minutes before invoking a boot option. If we * want to return to the boot manager, we have to disable the * watchdog timer and since we're an interactive program, we don't * want to wait until the user types "quit". The timer may have * fired by then. We don't care if this fails. It does not prevent * normal functioning in any way... */ BS->SetWatchdogTimer(0, 0, 0, NULL); if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &pool_guid) != 0) return (EFI_NOT_FOUND); switch (dev->dv_type) { #ifdef EFI_ZFS_BOOT case DEVT_ZFS: { struct zfs_devdesc currdev; currdev.d_dev = dev; currdev.d_unit = unit; currdev.d_type = currdev.d_dev->dv_type; currdev.d_opendata = NULL; currdev.pool_guid = pool_guid; currdev.root_guid = 0; env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, env_nounset); init_zfs_bootenv(zfs_fmtdev(&currdev)); break; } #endif default: { struct devdesc currdev; currdev.d_dev = dev; currdev.d_unit = unit; currdev.d_opendata = NULL; currdev.d_type = currdev.d_dev->dv_type; env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, env_nounset); break; } } setenv("LINES", "24", 1); /* optional */ for (k = 0; k < ST->NumberOfTableEntries; k++) { guid = &ST->ConfigurationTable[k].VendorGuid; if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { smbios_detect(ST->ConfigurationTable[k].VendorTable); break; } } interact(NULL); /* doesn't return */ return (EFI_SUCCESS); /* keep compiler happy */ } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 23, (CHAR16 *)"Reboot from the loader"); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(quit, "quit", "exit the loader", command_quit); static int command_quit(int argc, char *argv[]) { exit(0); return (CMD_OK); } COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); static int command_memmap(int argc, char *argv[]) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map, *p; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; static char *types[] = { "Reserved", "LoaderCode", "LoaderData", "BootServicesCode", "BootServicesData", "RuntimeServicesCode", "RuntimeServicesData", "ConventionalMemory", "UnusableMemory", "ACPIReclaimMemory", "ACPIMemoryNVS", "MemoryMappedIO", "MemoryMappedIOPortSpace", "PalCode" }; sz = 0; status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); if (status != EFI_BUFFER_TOO_SMALL) { printf("Can't determine memory map size\n"); return (CMD_ERROR); } map = malloc(sz); status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (EFI_ERROR(status)) { printf("Can't read memory map\n"); return (CMD_ERROR); } ndesc = sz / dsz; printf("%23s %12s %12s %8s %4s\n", "Type", "Physical", "Virtual", "#Pages", "Attr"); for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { printf("%23s %012jx %012jx %08jx ", types[p->Type], (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, (uintmax_t)p->NumberOfPages); if (p->Attribute & EFI_MEMORY_UC) printf("UC "); if (p->Attribute & EFI_MEMORY_WC) printf("WC "); if (p->Attribute & EFI_MEMORY_WT) printf("WT "); if (p->Attribute & EFI_MEMORY_WB) printf("WB "); if (p->Attribute & EFI_MEMORY_UCE) printf("UCE "); if (p->Attribute & EFI_MEMORY_WP) printf("WP "); if (p->Attribute & EFI_MEMORY_RP) printf("RP "); if (p->Attribute & EFI_MEMORY_XP) printf("XP "); printf("\n"); } return (CMD_OK); } COMMAND_SET(configuration, "configuration", "print configuration tables", command_configuration); static const char * guid_to_string(EFI_GUID *guid) { static char buf[40]; sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); return (buf); } static int command_configuration(int argc, char *argv[]) { UINTN i; printf("NumberOfTableEntries=%lu\n", (unsigned long)ST->NumberOfTableEntries); for (i = 0; i < ST->NumberOfTableEntries; i++) { EFI_GUID *guid; printf(" "); guid = &ST->ConfigurationTable[i].VendorGuid; if (!memcmp(guid, &mps, sizeof(EFI_GUID))) printf("MPS Table"); else if (!memcmp(guid, &acpi, sizeof(EFI_GUID))) printf("ACPI Table"); else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID))) printf("ACPI 2.0 Table"); else if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) printf("SMBIOS Table"); else if (!memcmp(guid, &dxe, sizeof(EFI_GUID))) printf("DXE Table"); else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID))) printf("HOB List Table"); else if (!memcmp(guid, &memtype, sizeof(EFI_GUID))) printf("Memory Type Information Table"); else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID))) printf("Debug Image Info Table"); else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID))) printf("FDT Table"); else printf("Unknown Table (%s)", guid_to_string(guid)); printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); } return (CMD_OK); } COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); static int command_mode(int argc, char *argv[]) { UINTN cols, rows; unsigned int mode; int i; char *cp; char rowenv[8]; EFI_STATUS status; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; extern void HO(void); conout = ST->ConOut; if (argc > 1) { mode = strtol(argv[1], &cp, 0); if (cp[0] != '\0') { printf("Invalid mode\n"); return (CMD_ERROR); } status = conout->QueryMode(conout, mode, &cols, &rows); if (EFI_ERROR(status)) { printf("invalid mode %d\n", mode); return (CMD_ERROR); } status = conout->SetMode(conout, mode); if (EFI_ERROR(status)) { printf("couldn't set mode %d\n", mode); return (CMD_ERROR); } sprintf(rowenv, "%u", (unsigned)rows); setenv("LINES", rowenv, 1); HO(); /* set cursor */ return (CMD_OK); } printf("Current mode: %d\n", conout->Mode->Mode); for (i = 0; i <= conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, (unsigned)rows); } if (i != 0) printf("Select a mode with the command \"mode \"\n"); return (CMD_OK); } COMMAND_SET(nvram, "nvram", "get or set NVRAM variables", command_nvram); static int command_nvram(int argc, char *argv[]) { CHAR16 var[128]; CHAR16 *data; EFI_STATUS status; EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; UINTN varsz, datasz, i; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; conout = ST->ConOut; /* Initiate the search */ status = RS->GetNextVariableName(&varsz, NULL, NULL); for (; status != EFI_NOT_FOUND; ) { status = RS->GetNextVariableName(&varsz, var, &varguid); //if (EFI_ERROR(status)) //break; conout->OutputString(conout, var); printf("="); datasz = 0; status = RS->GetVariable(var, &varguid, NULL, &datasz, NULL); /* XXX: check status */ data = malloc(datasz); status = RS->GetVariable(var, &varguid, NULL, &datasz, data); if (EFI_ERROR(status)) printf(""); else { for (i = 0; i < datasz; i++) { if (isalnum(data[i]) || isspace(data[i])) printf("%c", data[i]); else printf("\\x%02x", data[i]); } } /* XXX */ pager_output("\n"); free(data); } return (CMD_OK); } #ifdef EFI_ZFS_BOOT COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", command_lszfs); static int command_lszfs(int argc, char *argv[]) { int err; if (argc != 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } err = zfs_list(argv[1]); if (err != 0) { command_errmsg = strerror(err); return (CMD_ERROR); } return (CMD_OK); } COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", command_reloadbe); static int command_reloadbe(int argc, char *argv[]) { int err; char *root; if (argc > 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } if (argc == 2) { err = zfs_bootenv(argv[1]); } else { root = getenv("zfs_be_root"); if (root == NULL) { return (CMD_OK); } err = zfs_bootenv(root); } if (err != 0) { command_errmsg = strerror(err); return (CMD_ERROR); } return (CMD_OK); } #endif #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void) { EFI_HANDLE h; u_int unit; int i; char dname[SPECNAMELEN + 1]; uint64_t guid; unit = 0; h = efi_find_handle(&efipart_dev, 0); for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) { snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i); if (zfs_probe_dev(dname, &guid) == 0) (void)efi_handle_update_dev(h, &zfs_dev, unit++, guid); } } #endif Index: projects/release-pkg/sys/boot/ficl/words.c =================================================================== --- projects/release-pkg/sys/boot/ficl/words.c (revision 295422) +++ projects/release-pkg/sys/boot/ficl/words.c (revision 295423) @@ -1,5209 +1,5209 @@ /******************************************************************* ** w o r d s . c ** Forth Inspired Command Language ** ANS Forth CORE word-set written in C ** Author: John Sadler (john_sadler@alum.mit.edu) ** Created: 19 July 1997 ** $Id: words.c,v 1.17 2001/12/05 07:21:34 jsadler Exp $ *******************************************************************/ /* ** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu) ** All rights reserved. ** ** Get the latest Ficl release at http://ficl.sourceforge.net ** ** I am interested in hearing from anyone who uses ficl. If you have ** a problem, a success story, a defect, an enhancement request, or ** if you would like to contribute to the ficl release, please ** contact me by email at the address above. ** ** L I C E N S E and D I S C L A I M E R ** ** 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$ */ #ifdef TESTMAIN #include #include #include #include #else #include #endif #include #include "ficl.h" #include "math64.h" static void colonParen(FICL_VM *pVM); static void literalIm(FICL_VM *pVM); static int ficlParseWord(FICL_VM *pVM, STRINGINFO si); /* ** Control structure building words use these ** strings' addresses as markers on the stack to ** check for structure completion. */ static char doTag[] = "do"; static char colonTag[] = "colon"; static char leaveTag[] = "leave"; static char destTag[] = "target"; static char origTag[] = "origin"; static char caseTag[] = "case"; static char ofTag[] = "of"; static char fallthroughTag[] = "fallthrough"; #if FICL_WANT_LOCALS static void doLocalIm(FICL_VM *pVM); static void do2LocalIm(FICL_VM *pVM); #endif /* ** C O N T R O L S T R U C T U R E B U I L D E R S ** ** Push current dict location for later branch resolution. ** The location may be either a branch target or a patch address... */ static void markBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag) { PUSHPTR(dp->here); PUSHPTR(tag); return; } static void markControlTag(FICL_VM *pVM, char *tag) { PUSHPTR(tag); return; } static void matchControlTag(FICL_VM *pVM, char *tag) { char *cp; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif cp = (char *)stackPopPtr(pVM->pStack); /* ** Changed the code below to compare the pointers first (by popular demand) */ if ( (cp != tag) && strcmp(cp, tag) ) { vmThrowErr(pVM, "Error -- unmatched control structure \"%s\"", tag); } return; } /* ** Expect a branch target address on the param stack, ** compile a literal offset from the current dict location ** to the target address */ static void resolveBackBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag) { FICL_INT offset; CELL *patchAddr; matchControlTag(pVM, tag); #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif patchAddr = (CELL *)stackPopPtr(pVM->pStack); offset = patchAddr - dp->here; dictAppendCell(dp, LVALUEtoCELL(offset)); return; } /* ** Expect a branch patch address on the param stack, ** compile a literal offset from the patch location ** to the current dict location */ static void resolveForwardBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag) { FICL_INT offset; CELL *patchAddr; matchControlTag(pVM, tag); #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif patchAddr = (CELL *)stackPopPtr(pVM->pStack); offset = dp->here - patchAddr; *patchAddr = LVALUEtoCELL(offset); return; } /* ** Match the tag to the top of the stack. If success, ** sopy "here" address into the cell whose address is next ** on the stack. Used by do..leave..loop. */ static void resolveAbsBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag) { CELL *patchAddr; char *cp; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif cp = stackPopPtr(pVM->pStack); /* ** Changed the comparison below to compare the pointers first (by popular demand) */ if ((cp != tag) && strcmp(cp, tag)) { vmTextOut(pVM, "Warning -- Unmatched control word: ", 0); vmTextOut(pVM, tag, 1); } patchAddr = (CELL *)stackPopPtr(pVM->pStack); *patchAddr = LVALUEtoCELL(dp->here); return; } /************************************************************************** f i c l P a r s e N u m b e r ** Attempts to convert the NULL terminated string in the VM's pad to ** a number using the VM's current base. If successful, pushes the number ** onto the param stack and returns TRUE. Otherwise, returns FALSE. ** (jws 8/01) Trailing decimal point causes a zero cell to be pushed. (See ** the standard for DOUBLE wordset. **************************************************************************/ int ficlParseNumber(FICL_VM *pVM, STRINGINFO si) { FICL_INT accum = 0; char isNeg = FALSE; char hasDP = FALSE; unsigned base = pVM->base; char *cp = SI_PTR(si); FICL_COUNT count= (FICL_COUNT)SI_COUNT(si); unsigned ch; unsigned digit; if (count > 1) { switch (*cp) { case '-': cp++; count--; isNeg = TRUE; break; case '+': cp++; count--; isNeg = FALSE; break; default: break; } } if ((count > 0) && (cp[count-1] == '.')) /* detect & remove trailing decimal */ { hasDP = TRUE; count--; } if (count == 0) /* detect "+", "-", ".", "+." etc */ return FALSE; while ((count--) && ((ch = *cp++) != '\0')) { if (!isalnum(ch)) return FALSE; digit = ch - '0'; if (digit > 9) digit = tolower(ch) - 'a' + 10; if (digit >= base) return FALSE; accum = accum * base + digit; } if (hasDP) /* simple (required) DOUBLE support */ PUSHINT(0); if (isNeg) accum = -accum; PUSHINT(accum); if (pVM->state == COMPILE) literalIm(pVM); return TRUE; } /************************************************************************** a d d & f r i e n d s ** **************************************************************************/ static void add(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif i = stackPopINT(pVM->pStack); i += stackGetTop(pVM->pStack).i; stackSetTop(pVM->pStack, LVALUEtoCELL(i)); return; } static void sub(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif i = stackPopINT(pVM->pStack); i = stackGetTop(pVM->pStack).i - i; stackSetTop(pVM->pStack, LVALUEtoCELL(i)); return; } static void mul(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif i = stackPopINT(pVM->pStack); i *= stackGetTop(pVM->pStack).i; stackSetTop(pVM->pStack, LVALUEtoCELL(i)); return; } static void negate(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif i = -stackPopINT(pVM->pStack); PUSHINT(i); return; } static void ficlDiv(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif i = stackPopINT(pVM->pStack); i = stackGetTop(pVM->pStack).i / i; stackSetTop(pVM->pStack, LVALUEtoCELL(i)); return; } /* ** slash-mod CORE ( n1 n2 -- n3 n4 ) ** Divide n1 by n2, giving the single-cell remainder n3 and the single-cell ** quotient n4. An ambiguous condition exists if n2 is zero. If n1 and n2 ** differ in sign, the implementation-defined result returned will be the ** same as that returned by either the phrase ** >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM . ** NOTE: Ficl complies with the second phrase (symmetric division) */ static void slashMod(FICL_VM *pVM) { DPINT n1; FICL_INT n2; INTQR qr; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 2); #endif n2 = stackPopINT(pVM->pStack); n1.lo = stackPopINT(pVM->pStack); i64Extend(n1); qr = m64SymmetricDivI(n1, n2); PUSHINT(qr.rem); PUSHINT(qr.quot); return; } static void onePlus(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif i = stackGetTop(pVM->pStack).i; i += 1; stackSetTop(pVM->pStack, LVALUEtoCELL(i)); return; } static void oneMinus(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif i = stackGetTop(pVM->pStack).i; i -= 1; stackSetTop(pVM->pStack, LVALUEtoCELL(i)); return; } static void twoMul(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif i = stackGetTop(pVM->pStack).i; i *= 2; stackSetTop(pVM->pStack, LVALUEtoCELL(i)); return; } static void twoDiv(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif i = stackGetTop(pVM->pStack).i; i >>= 1; stackSetTop(pVM->pStack, LVALUEtoCELL(i)); return; } static void mulDiv(FICL_VM *pVM) { FICL_INT x, y, z; DPINT prod; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif z = stackPopINT(pVM->pStack); y = stackPopINT(pVM->pStack); x = stackPopINT(pVM->pStack); prod = m64MulI(x,y); x = m64SymmetricDivI(prod, z).quot; PUSHINT(x); return; } static void mulDivRem(FICL_VM *pVM) { FICL_INT x, y, z; DPINT prod; INTQR qr; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 2); #endif z = stackPopINT(pVM->pStack); y = stackPopINT(pVM->pStack); x = stackPopINT(pVM->pStack); prod = m64MulI(x,y); qr = m64SymmetricDivI(prod, z); PUSHINT(qr.rem); PUSHINT(qr.quot); return; } /************************************************************************** c o l o n d e f i n i t i o n s ** Code to begin compiling a colon definition ** This function sets the state to COMPILE, then creates a ** new word whose name is the next word in the input stream ** and whose code is colonParen. **************************************************************************/ static void colon(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); STRINGINFO si = vmGetWord(pVM); dictCheckThreshold(dp); pVM->state = COMPILE; markControlTag(pVM, colonTag); dictAppendWord2(dp, si, colonParen, FW_DEFAULT | FW_SMUDGE); #if FICL_WANT_LOCALS pVM->pSys->nLocals = 0; #endif return; } /************************************************************************** c o l o n P a r e n ** This is the code that executes a colon definition. It assumes that the ** virtual machine is running a "next" loop (See the vm.c ** for its implementation of member function vmExecute()). The colon ** code simply copies the address of the first word in the list of words ** to interpret into IP after saving its old value. When we return to the ** "next" loop, the virtual machine will call the code for each word in ** turn. ** **************************************************************************/ static void colonParen(FICL_VM *pVM) { IPTYPE tempIP = (IPTYPE) (pVM->runningWord->param); vmPushIP(pVM, tempIP); return; } /************************************************************************** s e m i c o l o n C o I m ** ** IMMEDIATE code for ";". This function sets the state to INTERPRET and ** terminates a word under compilation by appending code for "(;)" to ** the definition. TO DO: checks for leftover branch target tags on the ** return stack and complains if any are found. **************************************************************************/ static void semiParen(FICL_VM *pVM) { vmPopIP(pVM); return; } static void semicolonCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pSemiParen); matchControlTag(pVM, colonTag); #if FICL_WANT_LOCALS assert(pVM->pSys->pUnLinkParen); if (pVM->pSys->nLocals > 0) { FICL_DICT *pLoc = ficlGetLoc(pVM->pSys); dictEmpty(pLoc, pLoc->pForthWords->size); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen)); } pVM->pSys->nLocals = 0; #endif dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pSemiParen)); pVM->state = INTERPRET; dictUnsmudge(dp); return; } /************************************************************************** e x i t ** CORE ** This function simply pops the previous instruction ** pointer and returns to the "next" loop. Used for exiting from within ** a definition. Note that exitParen is identical to semiParen - they ** are in two different functions so that "see" can correctly identify ** the end of a colon definition, even if it uses "exit". **************************************************************************/ static void exitParen(FICL_VM *pVM) { vmPopIP(pVM); return; } static void exitCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pExitParen); IGNORE(pVM); #if FICL_WANT_LOCALS if (pVM->pSys->nLocals > 0) { dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen)); } #endif dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pExitParen)); return; } /************************************************************************** c o n s t a n t P a r e n ** This is the run-time code for "constant". It simply returns the ** contents of its word's first data cell. ** **************************************************************************/ void constantParen(FICL_VM *pVM) { FICL_WORD *pFW = pVM->runningWord; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif stackPush(pVM->pStack, pFW->param[0]); return; } void twoConstParen(FICL_VM *pVM) { FICL_WORD *pFW = pVM->runningWord; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 2); #endif stackPush(pVM->pStack, pFW->param[0]); /* lo */ stackPush(pVM->pStack, pFW->param[1]); /* hi */ return; } /************************************************************************** c o n s t a n t ** IMMEDIATE ** Compiles a constant into the dictionary. Constants return their ** value when invoked. Expects a value on top of the parm stack. **************************************************************************/ static void constant(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); STRINGINFO si = vmGetWord(pVM); #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif dictAppendWord2(dp, si, constantParen, FW_DEFAULT); dictAppendCell(dp, stackPop(pVM->pStack)); return; } static void twoConstant(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); STRINGINFO si = vmGetWord(pVM); CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif c = stackPop(pVM->pStack); dictAppendWord2(dp, si, twoConstParen, FW_DEFAULT); dictAppendCell(dp, stackPop(pVM->pStack)); dictAppendCell(dp, c); return; } /************************************************************************** d i s p l a y C e l l ** Drop and print the contents of the cell at the top of the param ** stack **************************************************************************/ static void displayCell(FICL_VM *pVM) { CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif c = stackPop(pVM->pStack); ltoa((c).i, pVM->pad, pVM->base); strcat(pVM->pad, " "); vmTextOut(pVM, pVM->pad, 0); return; } static void uDot(FICL_VM *pVM) { FICL_UNS u; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif u = stackPopUNS(pVM->pStack); ultoa(u, pVM->pad, pVM->base); strcat(pVM->pad, " "); vmTextOut(pVM, pVM->pad, 0); return; } static void hexDot(FICL_VM *pVM) { FICL_UNS u; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif u = stackPopUNS(pVM->pStack); ultoa(u, pVM->pad, 16); strcat(pVM->pad, " "); vmTextOut(pVM, pVM->pad, 0); return; } /************************************************************************** s t r l e n ** FICL ( c-string -- length ) ** ** Returns the length of a C-style (zero-terminated) string. ** ** --lch **/ static void ficlStrlen(FICL_VM *ficlVM) { char *address = (char *)stackPopPtr(ficlVM->pStack); stackPushINT(ficlVM->pStack, strlen(address)); } /************************************************************************** s p r i n t f ** FICL ( i*x c-addr-fmt u-fmt c-addr-buffer u-buffer -- c-addr-buffer u-written success-flag ) ** Similar to the C sprintf() function. It formats into a buffer based on ** a "format" string. Each character in the format string is copied verbatim ** to the output buffer, until SPRINTF encounters a percent sign ("%"). ** SPRINTF then skips the percent sign, and examines the next character ** (the "format character"). Here are the valid format characters: ** s - read a C-ADDR U-LENGTH string from the stack and copy it to ** the buffer ** d - read a cell from the stack, format it as a string (base-10, ** signed), and copy it to the buffer ** x - same as d, except in base-16 ** u - same as d, but unsigned ** % - output a literal percent-sign to the buffer ** SPRINTF returns the c-addr-buffer argument unchanged, the number of bytes ** written, and a flag indicating whether or not it ran out of space while ** writing to the output buffer (TRUE if it ran out of space). ** ** If SPRINTF runs out of space in the buffer to store the formatted string, ** it still continues parsing, in an effort to preserve your stack (otherwise ** it might leave uneaten arguments behind). ** ** --lch **************************************************************************/ static void ficlSprintf(FICL_VM *pVM) /* */ { int bufferLength = stackPopINT(pVM->pStack); char *buffer = (char *)stackPopPtr(pVM->pStack); char *bufferStart = buffer; int formatLength = stackPopINT(pVM->pStack); char *format = (char *)stackPopPtr(pVM->pStack); char *formatStop = format + formatLength; int base = 10; int unsignedInteger = FALSE; FICL_INT append = FICL_TRUE; while (format < formatStop) { char scratch[64]; char *source; int actualLength; int desiredLength; int leadingZeroes; if (*format != '%') { source = format; actualLength = desiredLength = 1; leadingZeroes = 0; } else { format++; if (format == formatStop) break; leadingZeroes = (*format == '0'); if (leadingZeroes) { format++; if (format == formatStop) break; } desiredLength = isdigit(*format); if (desiredLength) { desiredLength = strtol(format, &format, 10); if (format == formatStop) break; } else if (*format == '*') { desiredLength = stackPopINT(pVM->pStack); format++; if (format == formatStop) break; } switch (*format) { case 's': case 'S': { actualLength = stackPopINT(pVM->pStack); source = (char *)stackPopPtr(pVM->pStack); break; } case 'x': case 'X': base = 16; case 'u': case 'U': unsignedInteger = TRUE; case 'd': case 'D': { int integer = stackPopINT(pVM->pStack); if (unsignedInteger) ultoa(integer, scratch, base); else ltoa(integer, scratch, base); base = 10; unsignedInteger = FALSE; source = scratch; actualLength = strlen(scratch); break; } case '%': source = format; actualLength = 1; default: continue; } } if (append != FICL_FALSE) { if (!desiredLength) desiredLength = actualLength; if (desiredLength > bufferLength) { append = FICL_FALSE; desiredLength = bufferLength; } while (desiredLength > actualLength) { *buffer++ = (char)((leadingZeroes) ? '0' : ' '); bufferLength--; desiredLength--; } memcpy(buffer, source, actualLength); buffer += actualLength; bufferLength -= actualLength; } format++; } stackPushPtr(pVM->pStack, bufferStart); stackPushINT(pVM->pStack, buffer - bufferStart); stackPushINT(pVM->pStack, append); } /************************************************************************** d u p & f r i e n d s ** **************************************************************************/ static void depth(FICL_VM *pVM) { int i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif i = stackDepth(pVM->pStack); PUSHINT(i); return; } static void drop(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif stackDrop(pVM->pStack, 1); return; } static void twoDrop(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif stackDrop(pVM->pStack, 2); return; } static void dup(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 2); #endif stackPick(pVM->pStack, 0); return; } static void twoDup(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 4); #endif stackPick(pVM->pStack, 1); stackPick(pVM->pStack, 1); return; } static void over(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 3); #endif stackPick(pVM->pStack, 1); return; } static void twoOver(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 4, 6); #endif stackPick(pVM->pStack, 3); stackPick(pVM->pStack, 3); return; } static void pick(FICL_VM *pVM) { CELL c = stackPop(pVM->pStack); #if FICL_ROBUST > 1 vmCheckStack(pVM, c.i+1, c.i+2); #endif stackPick(pVM->pStack, c.i); return; } static void questionDup(FICL_VM *pVM) { CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 2); #endif c = stackGetTop(pVM->pStack); if (c.i != 0) stackPick(pVM->pStack, 0); return; } static void roll(FICL_VM *pVM) { int i = stackPop(pVM->pStack).i; i = (i > 0) ? i : 0; #if FICL_ROBUST > 1 vmCheckStack(pVM, i+1, i+1); #endif stackRoll(pVM->pStack, i); return; } static void minusRoll(FICL_VM *pVM) { int i = stackPop(pVM->pStack).i; i = (i > 0) ? i : 0; #if FICL_ROBUST > 1 vmCheckStack(pVM, i+1, i+1); #endif stackRoll(pVM->pStack, -i); return; } static void rot(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 3); #endif stackRoll(pVM->pStack, 2); return; } static void swap(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 2); #endif stackRoll(pVM->pStack, 1); return; } static void twoSwap(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 4, 4); #endif stackRoll(pVM->pStack, 3); stackRoll(pVM->pStack, 3); return; } /************************************************************************** e m i t & f r i e n d s ** **************************************************************************/ static void emit(FICL_VM *pVM) { char *cp = pVM->pad; int i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif i = stackPopINT(pVM->pStack); cp[0] = (char)i; cp[1] = '\0'; vmTextOut(pVM, cp, 0); return; } static void cr(FICL_VM *pVM) { vmTextOut(pVM, "", 1); return; } static void commentLine(FICL_VM *pVM) { char *cp = vmGetInBuf(pVM); char *pEnd = vmGetInBufEnd(pVM); char ch = *cp; while ((cp != pEnd) && (ch != '\r') && (ch != '\n')) { ch = *++cp; } /* ** Cope with DOS or UNIX-style EOLs - ** Check for /r, /n, /r/n, or /n/r end-of-line sequences, ** and point cp to next char. If EOL is \0, we're done. */ if (cp != pEnd) { cp++; if ( (cp != pEnd) && (ch != *cp) && ((*cp == '\r') || (*cp == '\n')) ) cp++; } vmUpdateTib(pVM, cp); return; } /* ** paren CORE ** Compilation: Perform the execution semantics given below. ** Execution: ( "ccc" -- ) ** Parse ccc delimited by ) (right parenthesis). ( is an immediate word. ** The number of characters in ccc may be zero to the number of characters ** in the parse area. ** */ static void commentHang(FICL_VM *pVM) { vmParseStringEx(pVM, ')', 0); return; } /************************************************************************** F E T C H & S T O R E ** **************************************************************************/ static void fetch(FICL_VM *pVM) { CELL *pCell; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif pCell = (CELL *)stackPopPtr(pVM->pStack); stackPush(pVM->pStack, *pCell); return; } /* ** two-fetch CORE ( a-addr -- x1 x2 ) ** Fetch the cell pair x1 x2 stored at a-addr. x2 is stored at a-addr and ** x1 at the next consecutive cell. It is equivalent to the sequence ** DUP CELL+ @ SWAP @ . */ static void twoFetch(FICL_VM *pVM) { CELL *pCell; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 2); #endif pCell = (CELL *)stackPopPtr(pVM->pStack); stackPush(pVM->pStack, *pCell++); stackPush(pVM->pStack, *pCell); swap(pVM); return; } /* ** store CORE ( x a-addr -- ) ** Store x at a-addr. */ static void store(FICL_VM *pVM) { CELL *pCell; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif pCell = (CELL *)stackPopPtr(pVM->pStack); *pCell = stackPop(pVM->pStack); } /* ** two-store CORE ( x1 x2 a-addr -- ) ** Store the cell pair x1 x2 at a-addr, with x2 at a-addr and x1 at the ** next consecutive cell. It is equivalent to the sequence ** SWAP OVER ! CELL+ ! . */ static void twoStore(FICL_VM *pVM) { CELL *pCell; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 0); #endif pCell = (CELL *)stackPopPtr(pVM->pStack); *pCell++ = stackPop(pVM->pStack); *pCell = stackPop(pVM->pStack); } static void plusStore(FICL_VM *pVM) { CELL *pCell; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif pCell = (CELL *)stackPopPtr(pVM->pStack); pCell->i += stackPop(pVM->pStack).i; } static void quadFetch(FICL_VM *pVM) { UNS32 *pw; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif pw = (UNS32 *)stackPopPtr(pVM->pStack); PUSHUNS((FICL_UNS)*pw); return; } static void quadStore(FICL_VM *pVM) { UNS32 *pw; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif pw = (UNS32 *)stackPopPtr(pVM->pStack); *pw = (UNS32)(stackPop(pVM->pStack).u); } static void wFetch(FICL_VM *pVM) { UNS16 *pw; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif pw = (UNS16 *)stackPopPtr(pVM->pStack); PUSHUNS((FICL_UNS)*pw); return; } static void wStore(FICL_VM *pVM) { UNS16 *pw; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif pw = (UNS16 *)stackPopPtr(pVM->pStack); *pw = (UNS16)(stackPop(pVM->pStack).u); } static void cFetch(FICL_VM *pVM) { UNS8 *pc; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif pc = (UNS8 *)stackPopPtr(pVM->pStack); PUSHUNS((FICL_UNS)*pc); return; } static void cStore(FICL_VM *pVM) { UNS8 *pc; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif pc = (UNS8 *)stackPopPtr(pVM->pStack); *pc = (UNS8)(stackPop(pVM->pStack).u); } /************************************************************************** b r a n c h P a r e n ** ** Runtime for "(branch)" -- expects a literal offset in the next ** compilation address, and branches to that location. **************************************************************************/ static void branchParen(FICL_VM *pVM) { vmBranchRelative(pVM, (uintptr_t)*(pVM->ip)); return; } /************************************************************************** b r a n c h 0 ** Runtime code for "(branch0)"; pop a flag from the stack, ** branch if 0. fall through otherwise. The heart of "if" and "until". **************************************************************************/ static void branch0(FICL_VM *pVM) { FICL_UNS flag; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif flag = stackPopUNS(pVM->pStack); if (flag) { /* fall through */ vmBranchRelative(pVM, 1); } else { /* take branch (to else/endif/begin) */ vmBranchRelative(pVM, (uintptr_t)*(pVM->ip)); } return; } /************************************************************************** i f C o I m ** IMMEDIATE COMPILE-ONLY ** Compiles code for a conditional branch into the dictionary ** and pushes the branch patch address on the stack for later ** patching by ELSE or THEN/ENDIF. **************************************************************************/ static void ifCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pBranch0); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0)); markBranch(dp, pVM, origTag); dictAppendUNS(dp, 1); return; } /************************************************************************** e l s e C o I m ** ** IMMEDIATE COMPILE-ONLY ** compiles an "else"... ** 1) Compile a branch and a patch address; the address gets patched ** by "endif" to point past the "else" code. ** 2) Pop the "if" patch address ** 3) Patch the "if" branch to point to the current compile address. ** 4) Push the "else" patch address. ("endif" patches this to jump past ** the "else" code. **************************************************************************/ static void elseCoIm(FICL_VM *pVM) { CELL *patchAddr; FICL_INT offset; FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pBranchParen); /* (1) compile branch runtime */ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen)); matchControlTag(pVM, origTag); patchAddr = (CELL *)stackPopPtr(pVM->pStack); /* (2) pop "if" patch addr */ markBranch(dp, pVM, origTag); /* (4) push "else" patch addr */ dictAppendUNS(dp, 1); /* (1) compile patch placeholder */ offset = dp->here - patchAddr; *patchAddr = LVALUEtoCELL(offset); /* (3) Patch "if" */ return; } /************************************************************************** e n d i f C o I m ** IMMEDIATE COMPILE-ONLY **************************************************************************/ static void endifCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); resolveForwardBranch(dp, pVM, origTag); return; } /************************************************************************** c a s e C o I m ** IMMEDIATE COMPILE-ONLY ** ** ** At compile-time, a CASE-SYS (see DPANS94 6.2.0873) looks like this: ** i*addr i caseTag ** and an OF-SYS (see DPANS94 6.2.1950) looks like this: ** i*addr i caseTag addr ofTag ** The integer under caseTag is the count of fixup addresses that branch ** to ENDCASE. **************************************************************************/ static void caseCoIm(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 2); #endif PUSHUNS(0); markControlTag(pVM, caseTag); return; } /************************************************************************** e n d c a s eC o I m ** IMMEDIATE COMPILE-ONLY **************************************************************************/ static void endcaseCoIm(FICL_VM *pVM) { FICL_UNS fixupCount; FICL_DICT *dp; CELL *patchAddr; FICL_INT offset; assert(pVM->pSys->pDrop); /* ** if the last OF ended with FALLTHROUGH, ** just add the FALLTHROUGH fixup to the ** ENDOF fixups */ if (stackGetTop(pVM->pStack).p == fallthroughTag) { matchControlTag(pVM, fallthroughTag); patchAddr = POPPTR(); matchControlTag(pVM, caseTag); fixupCount = POPUNS(); PUSHPTR(patchAddr); PUSHUNS(fixupCount + 1); markControlTag(pVM, caseTag); } matchControlTag(pVM, caseTag); #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif fixupCount = POPUNS(); #if FICL_ROBUST > 1 vmCheckStack(pVM, fixupCount, 0); #endif dp = vmGetDict(pVM); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDrop)); while (fixupCount--) { patchAddr = (CELL *)stackPopPtr(pVM->pStack); offset = dp->here - patchAddr; *patchAddr = LVALUEtoCELL(offset); } return; } static void ofParen(FICL_VM *pVM) { FICL_UNS a, b; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif a = POPUNS(); b = stackGetTop(pVM->pStack).u; if (a == b) { /* fall through */ stackDrop(pVM->pStack, 1); vmBranchRelative(pVM, 1); } else { /* take branch to next of or endswitch */ vmBranchRelative(pVM, *(int *)(pVM->ip)); } return; } /************************************************************************** o f C o I m ** IMMEDIATE COMPILE-ONLY **************************************************************************/ static void ofCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); CELL *fallthroughFixup = NULL; assert(pVM->pSys->pBranch0); #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 3); #endif if (stackGetTop(pVM->pStack).p == fallthroughTag) { matchControlTag(pVM, fallthroughTag); fallthroughFixup = POPPTR(); } matchControlTag(pVM, caseTag); markControlTag(pVM, caseTag); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pOfParen)); markBranch(dp, pVM, ofTag); dictAppendUNS(dp, 2); if (fallthroughFixup != NULL) { FICL_INT offset = dp->here - fallthroughFixup; *fallthroughFixup = LVALUEtoCELL(offset); } return; } /************************************************************************** e n d o f C o I m ** IMMEDIATE COMPILE-ONLY **************************************************************************/ static void endofCoIm(FICL_VM *pVM) { CELL *patchAddr; FICL_UNS fixupCount; FICL_INT offset; FICL_DICT *dp = vmGetDict(pVM); #if FICL_ROBUST > 1 vmCheckStack(pVM, 4, 3); #endif assert(pVM->pSys->pBranchParen); /* ensure we're in an OF, */ matchControlTag(pVM, ofTag); /* grab the address of the branch location after the OF */ patchAddr = (CELL *)stackPopPtr(pVM->pStack); /* ensure we're also in a "case" */ matchControlTag(pVM, caseTag); /* grab the current number of ENDOF fixups */ fixupCount = POPUNS(); /* compile branch runtime */ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen)); /* push a new ENDOF fixup, the updated count of ENDOF fixups, and the caseTag */ PUSHPTR(dp->here); PUSHUNS(fixupCount + 1); markControlTag(pVM, caseTag); /* reserve space for the ENDOF fixup */ dictAppendUNS(dp, 2); /* and patch the original OF */ offset = dp->here - patchAddr; *patchAddr = LVALUEtoCELL(offset); } /************************************************************************** f a l l t h r o u g h C o I m ** IMMEDIATE COMPILE-ONLY **************************************************************************/ static void fallthroughCoIm(FICL_VM *pVM) { CELL *patchAddr; FICL_INT offset; FICL_DICT *dp = vmGetDict(pVM); #if FICL_ROBUST > 1 vmCheckStack(pVM, 4, 3); #endif /* ensure we're in an OF, */ matchControlTag(pVM, ofTag); /* grab the address of the branch location after the OF */ patchAddr = (CELL *)stackPopPtr(pVM->pStack); /* ensure we're also in a "case" */ matchControlTag(pVM, caseTag); /* okay, here we go. put the case tag back. */ markControlTag(pVM, caseTag); /* compile branch runtime */ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen)); /* push a new FALLTHROUGH fixup and the fallthroughTag */ PUSHPTR(dp->here); markControlTag(pVM, fallthroughTag); /* reserve space for the FALLTHROUGH fixup */ dictAppendUNS(dp, 2); /* and patch the original OF */ offset = dp->here - patchAddr; *patchAddr = LVALUEtoCELL(offset); } /************************************************************************** h a s h ** hash ( c-addr u -- code) ** calculates hashcode of specified string and leaves it on the stack **************************************************************************/ static void hash(FICL_VM *pVM) { STRINGINFO si; SI_SETLEN(si, stackPopUNS(pVM->pStack)); SI_SETPTR(si, stackPopPtr(pVM->pStack)); PUSHUNS(hashHashCode(si)); return; } /************************************************************************** i n t e r p r e t ** This is the "user interface" of a Forth. It does the following: ** while there are words in the VM's Text Input Buffer ** Copy next word into the pad (vmGetWord) ** Attempt to find the word in the dictionary (dictLookup) ** If successful, execute the word. ** Otherwise, attempt to convert the word to a number (isNumber) ** If successful, push the number onto the parameter stack. ** Otherwise, print an error message and exit loop... ** End Loop ** ** From the standard, section 3.4 ** Text interpretation (see 6.1.1360 EVALUATE and 6.1.2050 QUIT) shall ** repeat the following steps until either the parse area is empty or an ** ambiguous condition exists: ** a) Skip leading spaces and parse a name (see 3.4.1); **************************************************************************/ static void interpret(FICL_VM *pVM) { STRINGINFO si; int i; FICL_SYSTEM *pSys; assert(pVM); pSys = pVM->pSys; si = vmGetWord0(pVM); /* ** Get next word...if out of text, we're done. */ if (si.count == 0) { vmThrow(pVM, VM_OUTOFTEXT); } /* ** Attempt to find the incoming token in the dictionary. If that fails... ** run the parse chain against the incoming token until somebody eats it. ** Otherwise emit an error message and give up. ** Although ficlParseWord could be part of the parse list, I've hard coded it ** in for robustness. ficlInitSystem adds the other default steps to the list. */ if (ficlParseWord(pVM, si)) return; for (i=0; i < FICL_MAX_PARSE_STEPS; i++) { FICL_WORD *pFW = pSys->parseList[i]; if (pFW == NULL) break; if (pFW->code == parseStepParen) { FICL_PARSE_STEP pStep; pStep = (FICL_PARSE_STEP)(pFW->param->fn); if ((*pStep)(pVM, si)) return; } else { stackPushPtr(pVM->pStack, SI_PTR(si)); stackPushUNS(pVM->pStack, SI_COUNT(si)); ficlExecXT(pVM, pFW); if (stackPopINT(pVM->pStack)) return; } } i = SI_COUNT(si); vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si)); return; /* back to inner interpreter */ } /************************************************************************** f i c l P a r s e W o r d ** From the standard, section 3.4 ** b) Search the dictionary name space (see 3.4.2). If a definition name ** matching the string is found: ** 1.if interpreting, perform the interpretation semantics of the definition ** (see 3.4.3.2), and continue at a); ** 2.if compiling, perform the compilation semantics of the definition ** (see 3.4.3.3), and continue at a). ** ** c) If a definition name matching the string is not found, attempt to ** convert the string to a number (see 3.4.1.3). If successful: ** 1.if interpreting, place the number on the data stack, and continue at a); ** 2.if compiling, compile code that when executed will place the number on ** the stack (see 6.1.1780 LITERAL), and continue at a); ** ** d) If unsuccessful, an ambiguous condition exists (see 3.4.4). ** ** (jws 4/01) Modified to be a FICL_PARSE_STEP **************************************************************************/ static int ficlParseWord(FICL_VM *pVM, STRINGINFO si) { FICL_DICT *dp = vmGetDict(pVM); FICL_WORD *tempFW; #if FICL_ROBUST dictCheck(dp, pVM, 0); vmCheckStack(pVM, 0, 0); #endif #if FICL_WANT_LOCALS if (pVM->pSys->nLocals > 0) { tempFW = ficlLookupLoc(pVM->pSys, si); } else #endif tempFW = dictLookup(dp, si); if (pVM->state == INTERPRET) { if (tempFW != NULL) { if (wordIsCompileOnly(tempFW)) { vmThrowErr(pVM, "Error: Compile only!"); } vmExecute(pVM, tempFW); return (int)FICL_TRUE; } } else /* (pVM->state == COMPILE) */ { if (tempFW != NULL) { if (wordIsImmediate(tempFW)) { vmExecute(pVM, tempFW); } else { dictAppendCell(dp, LVALUEtoCELL(tempFW)); } return (int)FICL_TRUE; } } return FICL_FALSE; } /* ** Surrogate precompiled parse step for ficlParseWord (this step is hard coded in ** INTERPRET) */ static void lookup(FICL_VM *pVM) { STRINGINFO si; SI_SETLEN(si, stackPopUNS(pVM->pStack)); SI_SETPTR(si, stackPopPtr(pVM->pStack)); stackPushINT(pVM->pStack, ficlParseWord(pVM, si)); return; } /************************************************************************** p a r e n P a r s e S t e p ** (parse-step) ( c-addr u -- flag ) ** runtime for a precompiled parse step - pop a counted string off the ** stack, run the parse step against it, and push the result flag (FICL_TRUE ** if success, FICL_FALSE otherwise). **************************************************************************/ void parseStepParen(FICL_VM *pVM) { STRINGINFO si; FICL_WORD *pFW = pVM->runningWord; FICL_PARSE_STEP pStep = (FICL_PARSE_STEP)(pFW->param->fn); SI_SETLEN(si, stackPopINT(pVM->pStack)); SI_SETPTR(si, stackPopPtr(pVM->pStack)); PUSHINT((*pStep)(pVM, si)); return; } static void addParseStep(FICL_VM *pVM) { FICL_WORD *pStep; FICL_DICT *pd = vmGetDict(pVM); #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif pStep = (FICL_WORD *)(stackPop(pVM->pStack).p); if ((pStep != NULL) && isAFiclWord(pd, pStep)) ficlAddParseStep(pVM->pSys, pStep); return; } /************************************************************************** l i t e r a l P a r e n ** ** This is the runtime for (literal). It assumes that it is part of a colon ** definition, and that the next CELL contains a value to be pushed on the ** parameter stack at runtime. This code is compiled by "literal". ** **************************************************************************/ static void literalParen(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif PUSHINT(*(FICL_INT *)(pVM->ip)); vmBranchRelative(pVM, 1); return; } static void twoLitParen(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 2); #endif PUSHINT(*((FICL_INT *)(pVM->ip)+1)); PUSHINT(*(FICL_INT *)(pVM->ip)); vmBranchRelative(pVM, 2); return; } /************************************************************************** l i t e r a l I m ** ** IMMEDIATE code for "literal". This function gets a value from the stack ** and compiles it into the dictionary preceded by the code for "(literal)". ** IMMEDIATE **************************************************************************/ static void literalIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pLitParen); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pLitParen)); dictAppendCell(dp, stackPop(pVM->pStack)); return; } static void twoLiteralIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pTwoLitParen); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pTwoLitParen)); dictAppendCell(dp, stackPop(pVM->pStack)); dictAppendCell(dp, stackPop(pVM->pStack)); return; } /************************************************************************** l o g i c a n d c o m p a r i s o n s ** **************************************************************************/ static void zeroEquals(FICL_VM *pVM) { CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif c.i = FICL_BOOL(stackPopINT(pVM->pStack) == 0); stackPush(pVM->pStack, c); return; } static void zeroLess(FICL_VM *pVM) { CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif c.i = FICL_BOOL(stackPopINT(pVM->pStack) < 0); stackPush(pVM->pStack, c); return; } static void zeroGreater(FICL_VM *pVM) { CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif c.i = FICL_BOOL(stackPopINT(pVM->pStack) > 0); stackPush(pVM->pStack, c); return; } static void isEqual(FICL_VM *pVM) { CELL x, y; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif x = stackPop(pVM->pStack); y = stackPop(pVM->pStack); PUSHINT(FICL_BOOL(x.i == y.i)); return; } static void isLess(FICL_VM *pVM) { CELL x, y; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif y = stackPop(pVM->pStack); x = stackPop(pVM->pStack); PUSHINT(FICL_BOOL(x.i < y.i)); return; } static void uIsLess(FICL_VM *pVM) { FICL_UNS u1, u2; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif u2 = stackPopUNS(pVM->pStack); u1 = stackPopUNS(pVM->pStack); PUSHINT(FICL_BOOL(u1 < u2)); return; } static void isGreater(FICL_VM *pVM) { CELL x, y; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif y = stackPop(pVM->pStack); x = stackPop(pVM->pStack); PUSHINT(FICL_BOOL(x.i > y.i)); return; } static void bitwiseAnd(FICL_VM *pVM) { CELL x, y; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif x = stackPop(pVM->pStack); y = stackPop(pVM->pStack); PUSHINT(x.i & y.i); return; } static void bitwiseOr(FICL_VM *pVM) { CELL x, y; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif x = stackPop(pVM->pStack); y = stackPop(pVM->pStack); PUSHINT(x.i | y.i); return; } static void bitwiseXor(FICL_VM *pVM) { CELL x, y; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 1); #endif x = stackPop(pVM->pStack); y = stackPop(pVM->pStack); PUSHINT(x.i ^ y.i); return; } static void bitwiseNot(FICL_VM *pVM) { CELL x; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif x = stackPop(pVM->pStack); PUSHINT(~x.i); return; } /************************************************************************** D o / L o o p ** do -- IMMEDIATE COMPILE ONLY ** Compiles code to initialize a loop: compile (do), ** allot space to hold the "leave" address, push a branch ** target address for the loop. ** (do) -- runtime for "do" ** pops index and limit from the p stack and moves them ** to the r stack, then skips to the loop body. ** loop -- IMMEDIATE COMPILE ONLY ** +loop ** Compiles code for the test part of a loop: ** compile (loop), resolve forward branch from "do", and ** copy "here" address to the "leave" address allotted by "do" ** i,j,k -- COMPILE ONLY ** Runtime: Push loop indices on param stack (i is innermost loop...) ** Note: each loop has three values on the return stack: ** ( R: leave limit index ) ** "leave" is the absolute address of the next cell after the loop ** limit and index are the loop control variables. ** leave -- COMPILE ONLY ** Runtime: pop the loop control variables, then pop the ** "leave" address and jump (absolute) there. **************************************************************************/ static void doCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pDoParen); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDoParen)); /* ** Allot space for a pointer to the end ** of the loop - "leave" uses this... */ markBranch(dp, pVM, leaveTag); dictAppendUNS(dp, 0); /* ** Mark location of head of loop... */ markBranch(dp, pVM, doTag); return; } static void doParen(FICL_VM *pVM) { CELL index, limit; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif index = stackPop(pVM->pStack); limit = stackPop(pVM->pStack); /* copy "leave" target addr to stack */ stackPushPtr(pVM->rStack, *(pVM->ip++)); stackPush(pVM->rStack, limit); stackPush(pVM->rStack, index); return; } static void qDoCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pQDoParen); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pQDoParen)); /* ** Allot space for a pointer to the end ** of the loop - "leave" uses this... */ markBranch(dp, pVM, leaveTag); dictAppendUNS(dp, 0); /* ** Mark location of head of loop... */ markBranch(dp, pVM, doTag); return; } static void qDoParen(FICL_VM *pVM) { CELL index, limit; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif index = stackPop(pVM->pStack); limit = stackPop(pVM->pStack); /* copy "leave" target addr to stack */ stackPushPtr(pVM->rStack, *(pVM->ip++)); if (limit.u == index.u) { vmPopIP(pVM); } else { stackPush(pVM->rStack, limit); stackPush(pVM->rStack, index); } return; } /* ** Runtime code to break out of a do..loop construct ** Drop the loop control variables; the branch address ** past "loop" is next on the return stack. */ static void leaveCo(FICL_VM *pVM) { /* almost unloop */ stackDrop(pVM->rStack, 2); /* exit */ vmPopIP(pVM); return; } static void unloopCo(FICL_VM *pVM) { stackDrop(pVM->rStack, 3); return; } static void loopCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pLoopParen); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pLoopParen)); resolveBackBranch(dp, pVM, doTag); resolveAbsBranch(dp, pVM, leaveTag); return; } static void plusLoopCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pPLoopParen); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pPLoopParen)); resolveBackBranch(dp, pVM, doTag); resolveAbsBranch(dp, pVM, leaveTag); return; } static void loopParen(FICL_VM *pVM) { FICL_INT index = stackGetTop(pVM->rStack).i; FICL_INT limit = stackFetch(pVM->rStack, 1).i; index++; if (index >= limit) { stackDrop(pVM->rStack, 3); /* nuke the loop indices & "leave" addr */ vmBranchRelative(pVM, 1); /* fall through the loop */ } else { /* update index, branch to loop head */ stackSetTop(pVM->rStack, LVALUEtoCELL(index)); vmBranchRelative(pVM, (uintptr_t)*(pVM->ip)); } return; } static void plusLoopParen(FICL_VM *pVM) { FICL_INT index,limit,increment; int flag; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif index = stackGetTop(pVM->rStack).i; limit = stackFetch(pVM->rStack, 1).i; increment = POP().i; index += increment; if (increment < 0) flag = (index < limit); else flag = (index >= limit); if (flag) { stackDrop(pVM->rStack, 3); /* nuke the loop indices & "leave" addr */ vmBranchRelative(pVM, 1); /* fall through the loop */ } else { /* update index, branch to loop head */ stackSetTop(pVM->rStack, LVALUEtoCELL(index)); vmBranchRelative(pVM, (uintptr_t)*(pVM->ip)); } return; } static void loopICo(FICL_VM *pVM) { CELL index = stackGetTop(pVM->rStack); stackPush(pVM->pStack, index); return; } static void loopJCo(FICL_VM *pVM) { CELL index = stackFetch(pVM->rStack, 3); stackPush(pVM->pStack, index); return; } static void loopKCo(FICL_VM *pVM) { CELL index = stackFetch(pVM->rStack, 6); stackPush(pVM->pStack, index); return; } /************************************************************************** r e t u r n s t a c k ** **************************************************************************/ static void toRStack(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif stackPush(pVM->rStack, POP()); } static void fromRStack(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif PUSH(stackPop(pVM->rStack)); } static void fetchRStack(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif PUSH(stackGetTop(pVM->rStack)); } static void twoToR(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif stackRoll(pVM->pStack, 1); stackPush(pVM->rStack, stackPop(pVM->pStack)); stackPush(pVM->rStack, stackPop(pVM->pStack)); return; } static void twoRFrom(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 2); #endif stackPush(pVM->pStack, stackPop(pVM->rStack)); stackPush(pVM->pStack, stackPop(pVM->rStack)); stackRoll(pVM->pStack, 1); return; } static void twoRFetch(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 2); #endif stackPush(pVM->pStack, stackFetch(pVM->rStack, 1)); stackPush(pVM->pStack, stackFetch(pVM->rStack, 0)); return; } /************************************************************************** v a r i a b l e ** **************************************************************************/ static void variableParen(FICL_VM *pVM) { FICL_WORD *fw; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif fw = pVM->runningWord; PUSHPTR(fw->param); } static void variable(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); STRINGINFO si = vmGetWord(pVM); dictAppendWord2(dp, si, variableParen, FW_DEFAULT); dictAllotCells(dp, 1); return; } static void twoVariable(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); STRINGINFO si = vmGetWord(pVM); dictAppendWord2(dp, si, variableParen, FW_DEFAULT); dictAllotCells(dp, 2); return; } /************************************************************************** b a s e & f r i e n d s ** **************************************************************************/ static void base(FICL_VM *pVM) { CELL *pBase; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif pBase = (CELL *)(&pVM->base); stackPush(pVM->pStack, LVALUEtoCELL(pBase)); return; } static void decimal(FICL_VM *pVM) { pVM->base = 10; return; } static void hex(FICL_VM *pVM) { pVM->base = 16; return; } /************************************************************************** a l l o t & f r i e n d s ** **************************************************************************/ static void allot(FICL_VM *pVM) { FICL_DICT *dp; FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif dp = vmGetDict(pVM); i = POPINT(); #if FICL_ROBUST dictCheck(dp, pVM, i); #endif dictAllot(dp, i); return; } static void here(FICL_VM *pVM) { FICL_DICT *dp; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif dp = vmGetDict(pVM); PUSHPTR(dp->here); return; } static void comma(FICL_VM *pVM) { FICL_DICT *dp; CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif dp = vmGetDict(pVM); c = POP(); dictAppendCell(dp, c); return; } static void cComma(FICL_VM *pVM) { FICL_DICT *dp; char c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif dp = vmGetDict(pVM); c = (char)POPINT(); dictAppendChar(dp, c); return; } static void cells(FICL_VM *pVM) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif i = POPINT(); PUSHINT(i * (FICL_INT)sizeof (CELL)); return; } static void cellPlus(FICL_VM *pVM) { char *cp; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif cp = POPPTR(); PUSHPTR(cp + sizeof (CELL)); return; } /************************************************************************** t i c k ** tick CORE ( "name" -- xt ) ** Skip leading space delimiters. Parse name delimited by a space. Find ** name and return xt, the execution token for name. An ambiguous condition ** exists if name is not found. **************************************************************************/ void ficlTick(FICL_VM *pVM) { FICL_WORD *pFW = NULL; STRINGINFO si = vmGetWord(pVM); #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif pFW = dictLookup(vmGetDict(pVM), si); if (!pFW) { int i = SI_COUNT(si); vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si)); } PUSHPTR(pFW); return; } static void bracketTickCoIm(FICL_VM *pVM) { ficlTick(pVM); literalIm(pVM); return; } /************************************************************************** p o s t p o n e ** Lookup the next word in the input stream and compile code to ** insert it into definitions created by the resulting word ** (defers compilation, even of immediate words) **************************************************************************/ static void postponeCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); FICL_WORD *pFW; FICL_WORD *pComma = ficlLookup(pVM->pSys, ","); assert(pComma); ficlTick(pVM); pFW = stackGetTop(pVM->pStack).p; if (wordIsImmediate(pFW)) { dictAppendCell(dp, stackPop(pVM->pStack)); } else { literalIm(pVM); dictAppendCell(dp, LVALUEtoCELL(pComma)); } return; } /************************************************************************** e x e c u t e ** Pop an execution token (pointer to a word) off the stack and ** run it **************************************************************************/ static void execute(FICL_VM *pVM) { FICL_WORD *pFW; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif pFW = stackPopPtr(pVM->pStack); vmExecute(pVM, pFW); return; } /************************************************************************** i m m e d i a t e ** Make the most recently compiled word IMMEDIATE -- it executes even ** in compile state (most often used for control compiling words ** such as IF, THEN, etc) **************************************************************************/ static void immediate(FICL_VM *pVM) { IGNORE(pVM); dictSetImmediate(vmGetDict(pVM)); return; } static void compileOnly(FICL_VM *pVM) { IGNORE(pVM); dictSetFlags(vmGetDict(pVM), FW_COMPILE, 0); return; } static void setObjectFlag(FICL_VM *pVM) { IGNORE(pVM); dictSetFlags(vmGetDict(pVM), FW_ISOBJECT, 0); return; } static void isObject(FICL_VM *pVM) { FICL_INT flag; FICL_WORD *pFW = (FICL_WORD *)stackPopPtr(pVM->pStack); flag = ((pFW != NULL) && (pFW->flags & FW_ISOBJECT)) ? FICL_TRUE : FICL_FALSE; stackPushINT(pVM->pStack, flag); return; } static void cstringLit(FICL_VM *pVM) { FICL_STRING *sp = (FICL_STRING *)(pVM->ip); char *cp = sp->text; cp += sp->count + 1; cp = alignPtr(cp); pVM->ip = (IPTYPE)(void *)cp; stackPushPtr(pVM->pStack, sp); return; } static void cstringQuoteIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); if (pVM->state == INTERPRET) { FICL_STRING *sp = (FICL_STRING *) dp->here; vmGetString(pVM, sp, '\"'); stackPushPtr(pVM->pStack, sp); /* move HERE past string so it doesn't get overwritten. --lch */ dictAllot(dp, sp->count + sizeof(FICL_COUNT)); } else /* COMPILE state */ { dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pCStringLit)); dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"'); dictAlign(dp); } return; } /************************************************************************** d o t Q u o t e ** IMMEDIATE word that compiles a string literal for later display ** Compile stringLit, then copy the bytes of the string from the TIB ** to the dictionary. Backpatch the count byte and align the dictionary. ** ** stringlit: Fetch the count from the dictionary, then push the address ** and count on the stack. Finally, update ip to point to the first ** aligned address after the string text. **************************************************************************/ static void stringLit(FICL_VM *pVM) { FICL_STRING *sp; FICL_COUNT count; char *cp; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 2); #endif sp = (FICL_STRING *)(pVM->ip); count = sp->count; cp = sp->text; PUSHPTR(cp); PUSHUNS(count); cp += count + 1; cp = alignPtr(cp); pVM->ip = (IPTYPE)(void *)cp; } static void dotQuoteCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); FICL_WORD *pType = ficlLookup(pVM->pSys, "type"); assert(pType); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit)); dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"'); dictAlign(dp); dictAppendCell(dp, LVALUEtoCELL(pType)); return; } static void dotParen(FICL_VM *pVM) { char *pSrc = vmGetInBuf(pVM); char *pEnd = vmGetInBufEnd(pVM); char *pDest = pVM->pad; char ch; /* ** Note: the standard does not want leading spaces skipped (apparently) */ for (ch = *pSrc; (pEnd != pSrc) && (ch != ')'); ch = *++pSrc) *pDest++ = ch; *pDest = '\0'; if ((pEnd != pSrc) && (ch == ')')) pSrc++; vmTextOut(pVM, pVM->pad, 0); vmUpdateTib(pVM, pSrc); return; } /************************************************************************** s l i t e r a l ** STRING ** Interpretation: Interpretation semantics for this word are undefined. ** Compilation: ( c-addr1 u -- ) ** Append the run-time semantics given below to the current definition. ** Run-time: ( -- c-addr2 u ) ** Return c-addr2 u describing a string consisting of the characters ** specified by c-addr1 u during compilation. A program shall not alter ** the returned string. **************************************************************************/ static void sLiteralCoIm(FICL_VM *pVM) { FICL_DICT *dp; char *cp, *cpDest; FICL_UNS u; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif dp = vmGetDict(pVM); u = POPUNS(); cp = POPPTR(); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit)); cpDest = (char *) dp->here; *cpDest++ = (char) u; for (; u > 0; --u) { *cpDest++ = *cp++; } *cpDest++ = 0; dp->here = PTRtoCELL alignPtr(cpDest); return; } /************************************************************************** s t a t e ** Return the address of the VM's state member (must be sized the ** same as a CELL for this reason) **************************************************************************/ static void state(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif PUSHPTR(&pVM->state); return; } /************************************************************************** c r e a t e . . . d o e s > ** Make a new word in the dictionary with the run-time effect of ** a variable (push my address), but with extra space allotted ** for use by does> . **************************************************************************/ static void createParen(FICL_VM *pVM) { CELL *pCell; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif pCell = pVM->runningWord->param; PUSHPTR(pCell+1); return; } static void create(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); STRINGINFO si = vmGetWord(pVM); dictCheckThreshold(dp); dictAppendWord2(dp, si, createParen, FW_DEFAULT); dictAllotCells(dp, 1); return; } static void doDoes(FICL_VM *pVM) { CELL *pCell; IPTYPE tempIP; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif pCell = pVM->runningWord->param; tempIP = (IPTYPE)((*pCell).p); PUSHPTR(pCell+1); vmPushIP(pVM, tempIP); return; } static void doesParen(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); dp->smudge->code = doDoes; dp->smudge->param[0] = LVALUEtoCELL(pVM->ip); vmPopIP(pVM); return; } static void doesCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); #if FICL_WANT_LOCALS assert(pVM->pSys->pUnLinkParen); if (pVM->pSys->nLocals > 0) { FICL_DICT *pLoc = ficlGetLoc(pVM->pSys); dictEmpty(pLoc, pLoc->pForthWords->size); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen)); } pVM->pSys->nLocals = 0; #endif IGNORE(pVM); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDoesParen)); return; } /************************************************************************** t o b o d y ** to-body CORE ( xt -- a-addr ) ** a-addr is the data-field address corresponding to xt. An ambiguous ** condition exists if xt is not for a word defined via CREATE. **************************************************************************/ static void toBody(FICL_VM *pVM) { FICL_WORD *pFW; /*#$-GUY CHANGE: Added robustness.-$#*/ #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif pFW = POPPTR(); PUSHPTR(pFW->param + 1); return; } /* ** from-body ficl ( a-addr -- xt ) ** Reverse effect of >body */ static void fromBody(FICL_VM *pVM) { char *ptr; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif ptr = (char *)POPPTR() - sizeof (FICL_WORD); PUSHPTR(ptr); return; } /* ** >name ficl ( xt -- c-addr u ) ** Push the address and length of a word's name given its address ** xt. */ static void toName(FICL_VM *pVM) { FICL_WORD *pFW; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 2); #endif pFW = POPPTR(); PUSHPTR(pFW->name); PUSHUNS(pFW->nName); return; } static void getLastWord(FICL_VM *pVM) { FICL_DICT *pDict = vmGetDict(pVM); FICL_WORD *wp = pDict->smudge; assert(wp); vmPush(pVM, LVALUEtoCELL(wp)); return; } /************************************************************************** l b r a c k e t e t c ** **************************************************************************/ static void lbracketCoIm(FICL_VM *pVM) { pVM->state = INTERPRET; return; } static void rbracket(FICL_VM *pVM) { pVM->state = COMPILE; return; } /************************************************************************** p i c t u r e d n u m e r i c w o r d s ** ** less-number-sign CORE ( -- ) ** Initialize the pictured numeric output conversion process. ** (clear the pad) **************************************************************************/ static void lessNumberSign(FICL_VM *pVM) { FICL_STRING *sp = PTRtoSTRING pVM->pad; sp->count = 0; return; } /* ** number-sign CORE ( ud1 -- ud2 ) ** Divide ud1 by the number in BASE giving the quotient ud2 and the remainder ** n. (n is the least-significant digit of ud1.) Convert n to external form ** and add the resulting character to the beginning of the pictured numeric ** output string. An ambiguous condition exists if # executes outside of a ** <# #> delimited number conversion. */ static void numberSign(FICL_VM *pVM) { FICL_STRING *sp; DPUNS u; UNS16 rem; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 2); #endif sp = PTRtoSTRING pVM->pad; u = u64Pop(pVM->pStack); rem = m64UMod(&u, (UNS16)(pVM->base)); sp->text[sp->count++] = digit_to_char(rem); u64Push(pVM->pStack, u); return; } /* ** number-sign-greater CORE ( xd -- c-addr u ) ** Drop xd. Make the pictured numeric output string available as a character ** string. c-addr and u specify the resulting character string. A program ** may replace characters within the string. */ static void numberSignGreater(FICL_VM *pVM) { FICL_STRING *sp; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 2); #endif sp = PTRtoSTRING pVM->pad; sp->text[sp->count] = 0; strrev(sp->text); DROP(2); PUSHPTR(sp->text); PUSHUNS(sp->count); return; } /* ** number-sign-s CORE ( ud1 -- ud2 ) ** Convert one digit of ud1 according to the rule for #. Continue conversion ** until the quotient is zero. ud2 is zero. An ambiguous condition exists if ** #S executes outside of a <# #> delimited number conversion. ** TO DO: presently does not use ud1 hi cell - use it! */ static void numberSignS(FICL_VM *pVM) { FICL_STRING *sp; DPUNS u; UNS16 rem; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 2); #endif sp = PTRtoSTRING pVM->pad; u = u64Pop(pVM->pStack); do { rem = m64UMod(&u, (UNS16)(pVM->base)); sp->text[sp->count++] = digit_to_char(rem); } while (u.hi || u.lo); u64Push(pVM->pStack, u); return; } /* ** HOLD CORE ( char -- ) ** Add char to the beginning of the pictured numeric output string. An ambiguous ** condition exists if HOLD executes outside of a <# #> delimited number conversion. */ static void hold(FICL_VM *pVM) { FICL_STRING *sp; int i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif sp = PTRtoSTRING pVM->pad; i = POPINT(); sp->text[sp->count++] = (char) i; return; } /* ** SIGN CORE ( n -- ) ** If n is negative, add a minus sign to the beginning of the pictured ** numeric output string. An ambiguous condition exists if SIGN ** executes outside of a <# #> delimited number conversion. */ static void sign(FICL_VM *pVM) { FICL_STRING *sp; int i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif sp = PTRtoSTRING pVM->pad; i = POPINT(); if (i < 0) sp->text[sp->count++] = '-'; return; } /************************************************************************** t o N u m b e r ** to-number CORE ( ud1 c-addr1 u1 -- ud2 c-addr2 u2 ) ** ud2 is the unsigned result of converting the characters within the ** string specified by c-addr1 u1 into digits, using the number in BASE, ** and adding each into ud1 after multiplying ud1 by the number in BASE. ** Conversion continues left-to-right until a character that is not ** convertible, including any + or -, is encountered or the string is ** entirely converted. c-addr2 is the location of the first unconverted ** character or the first character past the end of the string if the string ** was entirely converted. u2 is the number of unconverted characters in the ** string. An ambiguous condition exists if ud2 overflows during the ** conversion. **************************************************************************/ static void toNumber(FICL_VM *pVM) { FICL_UNS count; char *cp; DPUNS accum; FICL_UNS base = pVM->base; FICL_UNS ch; FICL_UNS digit; #if FICL_ROBUST > 1 vmCheckStack(pVM,4,4); #endif count = POPUNS(); cp = (char *)POPPTR(); accum = u64Pop(pVM->pStack); for (ch = *cp; count > 0; ch = *++cp, count--) { if (ch < '0') break; digit = ch - '0'; if (digit > 9) digit = tolower(ch) - 'a' + 10; /* ** Note: following test also catches chars between 9 and a ** because 'digit' is unsigned! */ if (digit >= base) break; accum = m64Mac(accum, base, digit); } u64Push(pVM->pStack, accum); PUSHPTR(cp); PUSHUNS(count); return; } /************************************************************************** q u i t & a b o r t ** quit CORE ( -- ) ( R: i*x -- ) ** Empty the return stack, store zero in SOURCE-ID if it is present, make ** the user input device the input source, and enter interpretation state. ** Do not display a message. Repeat the following: ** ** Accept a line from the input source into the input buffer, set >IN to ** zero, and interpret. ** Display the implementation-defined system prompt if in ** interpretation state, all processing has been completed, and no ** ambiguous condition exists. **************************************************************************/ static void quit(FICL_VM *pVM) { vmThrow(pVM, VM_QUIT); return; } static void ficlAbort(FICL_VM *pVM) { vmThrow(pVM, VM_ABORT); return; } /************************************************************************** a c c e p t ** accept CORE ( c-addr +n1 -- +n2 ) ** Receive a string of at most +n1 characters. An ambiguous condition ** exists if +n1 is zero or greater than 32,767. Display graphic characters ** as they are received. A program that depends on the presence or absence ** of non-graphic characters in the string has an environmental dependency. ** The editing functions, if any, that the system performs in order to ** construct the string are implementation-defined. ** ** (Although the standard text doesn't say so, I assume that the intent ** of 'accept' is to store the string at the address specified on ** the stack.) ** Implementation: if there's more text in the TIB, use it. Otherwise ** throw out for more text. Copy characters up to the max count into the ** address given, and return the number of actual characters copied. ** ** Note (sobral) this may not be the behavior you'd expect if you're ** trying to get user input at load time! **************************************************************************/ static void accept(FICL_VM *pVM) { FICL_UNS count, len; char *cp; char *pBuf, *pEnd; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,1); #endif pBuf = vmGetInBuf(pVM); pEnd = vmGetInBufEnd(pVM); len = pEnd - pBuf; if (len == 0) vmThrow(pVM, VM_RESTART); /* ** Now we have something in the text buffer - use it */ count = stackPopINT(pVM->pStack); cp = stackPopPtr(pVM->pStack); len = (count < len) ? count : len; strncpy(cp, vmGetInBuf(pVM), len); pBuf += len; vmUpdateTib(pVM, pBuf); PUSHINT(len); return; } /************************************************************************** a l i g n ** 6.1.0705 ALIGN CORE ( -- ) ** If the data-space pointer is not aligned, reserve enough space to ** align it. **************************************************************************/ static void align(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); IGNORE(pVM); dictAlign(dp); return; } /************************************************************************** a l i g n e d ** **************************************************************************/ static void aligned(FICL_VM *pVM) { void *addr; #if FICL_ROBUST > 1 vmCheckStack(pVM,1,1); #endif addr = POPPTR(); PUSHPTR(alignPtr(addr)); return; } /************************************************************************** b e g i n & f r i e n d s ** Indefinite loop control structures ** A.6.1.0760 BEGIN ** Typical use: ** : X ... BEGIN ... test UNTIL ; ** or ** : X ... BEGIN ... test WHILE ... REPEAT ; **************************************************************************/ static void beginCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); markBranch(dp, pVM, destTag); return; } static void untilCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pBranch0); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0)); resolveBackBranch(dp, pVM, destTag); return; } static void whileCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pBranch0); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0)); markBranch(dp, pVM, origTag); twoSwap(pVM); dictAppendUNS(dp, 1); return; } static void repeatCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pBranchParen); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen)); /* expect "begin" branch marker */ resolveBackBranch(dp, pVM, destTag); /* expect "while" branch marker */ resolveForwardBranch(dp, pVM, origTag); return; } static void againCoIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); assert(pVM->pSys->pBranchParen); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen)); /* expect "begin" branch marker */ resolveBackBranch(dp, pVM, destTag); return; } /************************************************************************** c h a r & f r i e n d s ** 6.1.0895 CHAR CORE ( "name" -- char ) ** Skip leading space delimiters. Parse name delimited by a space. ** Put the value of its first character onto the stack. ** ** bracket-char CORE ** Interpretation: Interpretation semantics for this word are undefined. ** Compilation: ( "name" -- ) ** Skip leading space delimiters. Parse name delimited by a space. ** Append the run-time semantics given below to the current definition. ** Run-time: ( -- char ) ** Place char, the value of the first character of name, on the stack. **************************************************************************/ static void ficlChar(FICL_VM *pVM) { STRINGINFO si; #if FICL_ROBUST > 1 vmCheckStack(pVM,0,1); #endif si = vmGetWord(pVM); PUSHUNS((FICL_UNS)(si.cp[0])); return; } static void charCoIm(FICL_VM *pVM) { ficlChar(pVM); literalIm(pVM); return; } /************************************************************************** c h a r P l u s ** char-plus CORE ( c-addr1 -- c-addr2 ) ** Add the size in address units of a character to c-addr1, giving c-addr2. **************************************************************************/ static void charPlus(FICL_VM *pVM) { char *cp; #if FICL_ROBUST > 1 vmCheckStack(pVM,1,1); #endif cp = POPPTR(); PUSHPTR(cp + 1); return; } /************************************************************************** c h a r s ** chars CORE ( n1 -- n2 ) ** n2 is the size in address units of n1 characters. ** For most processors, this function can be a no-op. To guarantee ** portability, we'll multiply by sizeof (char). **************************************************************************/ #if defined (_M_IX86) #pragma warning(disable: 4127) #endif static void ficlChars(FICL_VM *pVM) { if (sizeof (char) > 1) { FICL_INT i; #if FICL_ROBUST > 1 vmCheckStack(pVM,1,1); #endif i = POPINT(); PUSHINT(i * sizeof (char)); } /* otherwise no-op! */ return; } #if defined (_M_IX86) #pragma warning(default: 4127) #endif /************************************************************************** c o u n t ** COUNT CORE ( c-addr1 -- c-addr2 u ) ** Return the character string specification for the counted string stored ** at c-addr1. c-addr2 is the address of the first character after c-addr1. ** u is the contents of the character at c-addr1, which is the length in ** characters of the string at c-addr2. **************************************************************************/ static void count(FICL_VM *pVM) { FICL_STRING *sp; #if FICL_ROBUST > 1 vmCheckStack(pVM,1,2); #endif sp = POPPTR(); PUSHPTR(sp->text); PUSHUNS(sp->count); return; } /************************************************************************** e n v i r o n m e n t ? ** environment-query CORE ( c-addr u -- false | i*x true ) ** c-addr is the address of a character string and u is the string's ** character count. u may have a value in the range from zero to an ** implementation-defined maximum which shall not be less than 31. The ** character string should contain a keyword from 3.2.6 Environmental ** queries or the optional word sets to be checked for correspondence ** with an attribute of the present environment. If the system treats the ** attribute as unknown, the returned flag is false; otherwise, the flag ** is true and the i*x returned is of the type specified in the table for ** the attribute queried. **************************************************************************/ static void environmentQ(FICL_VM *pVM) { FICL_DICT *envp; FICL_WORD *pFW; STRINGINFO si; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,1); #endif envp = pVM->pSys->envp; si.count = (FICL_COUNT)stackPopUNS(pVM->pStack); si.cp = stackPopPtr(pVM->pStack); pFW = dictLookup(envp, si); if (pFW != NULL) { vmExecute(pVM, pFW); PUSHINT(FICL_TRUE); } else { PUSHINT(FICL_FALSE); } return; } /************************************************************************** e v a l u a t e ** EVALUATE CORE ( i*x c-addr u -- j*x ) ** Save the current input source specification. Store minus-one (-1) in ** SOURCE-ID if it is present. Make the string described by c-addr and u ** both the input source and input buffer, set >IN to zero, and interpret. ** When the parse area is empty, restore the prior input source ** specification. Other stack effects are due to the words EVALUATEd. ** **************************************************************************/ static void evaluate(FICL_VM *pVM) { FICL_UNS count; char *cp; CELL id; int result; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,0); #endif count = POPUNS(); cp = POPPTR(); IGNORE(count); id = pVM->sourceID; pVM->sourceID.i = -1; result = ficlExecC(pVM, cp, count); pVM->sourceID = id; if (result != VM_OUTOFTEXT) vmThrow(pVM, result); return; } /************************************************************************** s t r i n g q u o t e ** Interpreting: get string delimited by a quote from the input stream, ** copy to a scratch area, and put its count and address on the stack. ** Compiling: compile code to push the address and count of a string ** literal, compile the string from the input stream, and align the dict ** pointer. **************************************************************************/ static void stringQuoteIm(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); if (pVM->state == INTERPRET) { FICL_STRING *sp = (FICL_STRING *) dp->here; vmGetString(pVM, sp, '\"'); PUSHPTR(sp->text); PUSHUNS(sp->count); } else /* COMPILE state */ { dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit)); dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"'); dictAlign(dp); } return; } /************************************************************************** t y p e ** Pop count and char address from stack and print the designated string. **************************************************************************/ static void type(FICL_VM *pVM) { FICL_UNS count = stackPopUNS(pVM->pStack); char *cp = stackPopPtr(pVM->pStack); char *pDest = (char *)ficlMalloc(count + 1); /* ** Since we don't have an output primitive for a counted string ** (oops), make sure the string is null terminated. If not, copy ** and terminate it. */ if (!pDest) vmThrowErr(pVM, "Error: out of memory"); strncpy(pDest, cp, count); pDest[count] = '\0'; vmTextOut(pVM, pDest, 0); ficlFree(pDest); return; } /************************************************************************** w o r d ** word CORE ( char "ccc" -- c-addr ) ** Skip leading delimiters. Parse characters ccc delimited by char. An ** ambiguous condition exists if the length of the parsed string is greater ** than the implementation-defined length of a counted string. ** ** c-addr is the address of a transient region containing the parsed word ** as a counted string. If the parse area was empty or contained no ** characters other than the delimiter, the resulting string has a zero ** length. A space, not included in the length, follows the string. A ** program may replace characters within the string. ** NOTE! Ficl also NULL-terminates the dest string. **************************************************************************/ static void ficlWord(FICL_VM *pVM) { FICL_STRING *sp; char delim; STRINGINFO si; #if FICL_ROBUST > 1 vmCheckStack(pVM,1,1); #endif sp = (FICL_STRING *)pVM->pad; delim = (char)POPINT(); si = vmParseStringEx(pVM, delim, 1); if (SI_COUNT(si) > nPAD-1) SI_SETLEN(si, nPAD-1); sp->count = (FICL_COUNT)SI_COUNT(si); strncpy(sp->text, SI_PTR(si), SI_COUNT(si)); /*#$-GUY CHANGE: I added this.-$#*/ sp->text[sp->count] = 0; strcat(sp->text, " "); PUSHPTR(sp); return; } /************************************************************************** p a r s e - w o r d ** ficl PARSE-WORD ( name -- c-addr u ) ** Skip leading spaces and parse name delimited by a space. c-addr is the ** address within the input buffer and u is the length of the selected ** string. If the parse area is empty, the resulting string has a zero length. **************************************************************************/ static void parseNoCopy(FICL_VM *pVM) { STRINGINFO si; #if FICL_ROBUST > 1 vmCheckStack(pVM,0,2); #endif si = vmGetWord0(pVM); PUSHPTR(SI_PTR(si)); PUSHUNS(SI_COUNT(si)); return; } /************************************************************************** p a r s e ** CORE EXT ( char "ccc" -- c-addr u ) ** Parse ccc delimited by the delimiter char. ** c-addr is the address (within the input buffer) and u is the length of ** the parsed string. If the parse area was empty, the resulting string has ** a zero length. ** NOTE! PARSE differs from WORD: it does not skip leading delimiters. **************************************************************************/ static void parse(FICL_VM *pVM) { STRINGINFO si; char delim; #if FICL_ROBUST > 1 vmCheckStack(pVM,1,2); #endif delim = (char)POPINT(); si = vmParseStringEx(pVM, delim, 0); PUSHPTR(SI_PTR(si)); PUSHUNS(SI_COUNT(si)); return; } /************************************************************************** f i l l ** CORE ( c-addr u char -- ) ** If u is greater than zero, store char in each of u consecutive ** characters of memory beginning at c-addr. **************************************************************************/ static void fill(FICL_VM *pVM) { char ch; FICL_UNS u; char *cp; #if FICL_ROBUST > 1 vmCheckStack(pVM,3,0); #endif ch = (char)POPINT(); u = POPUNS(); cp = (char *)POPPTR(); while (u > 0) { *cp++ = ch; u--; } return; } /************************************************************************** f i n d ** FIND CORE ( c-addr -- c-addr 0 | xt 1 | xt -1 ) ** Find the definition named in the counted string at c-addr. If the ** definition is not found, return c-addr and zero. If the definition is ** found, return its execution token xt. If the definition is immediate, ** also return one (1), otherwise also return minus-one (-1). For a given ** string, the values returned by FIND while compiling may differ from ** those returned while not compiling. **************************************************************************/ static void do_find(FICL_VM *pVM, STRINGINFO si, void *returnForFailure) { FICL_WORD *pFW; pFW = dictLookup(vmGetDict(pVM), si); if (pFW) { PUSHPTR(pFW); PUSHINT((wordIsImmediate(pFW) ? 1 : -1)); } else { PUSHPTR(returnForFailure); PUSHUNS(0); } return; } /************************************************************************** f i n d ** FIND CORE ( c-addr -- c-addr 0 | xt 1 | xt -1 ) ** Find the definition named in the counted string at c-addr. If the ** definition is not found, return c-addr and zero. If the definition is ** found, return its execution token xt. If the definition is immediate, ** also return one (1), otherwise also return minus-one (-1). For a given ** string, the values returned by FIND while compiling may differ from ** those returned while not compiling. **************************************************************************/ static void cFind(FICL_VM *pVM) { FICL_STRING *sp; STRINGINFO si; #if FICL_ROBUST > 1 vmCheckStack(pVM,1,2); #endif sp = POPPTR(); SI_PFS(si, sp); do_find(pVM, si, sp); } /************************************************************************** s f i n d ** FICL ( c-addr u -- 0 0 | xt 1 | xt -1 ) ** Like FIND, but takes "c-addr u" for the string. **************************************************************************/ static void sFind(FICL_VM *pVM) { STRINGINFO si; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,2); #endif si.count = stackPopINT(pVM->pStack); si.cp = stackPopPtr(pVM->pStack); do_find(pVM, si, NULL); } /************************************************************************** f m S l a s h M o d ** f-m-slash-mod CORE ( d1 n1 -- n2 n3 ) ** Divide d1 by n1, giving the floored quotient n3 and the remainder n2. ** Input and output stack arguments are signed. An ambiguous condition ** exists if n1 is zero or if the quotient lies outside the range of a ** single-cell signed integer. **************************************************************************/ static void fmSlashMod(FICL_VM *pVM) { DPINT d1; FICL_INT n1; INTQR qr; #if FICL_ROBUST > 1 vmCheckStack(pVM,3,2); #endif n1 = POPINT(); d1 = i64Pop(pVM->pStack); qr = m64FlooredDivI(d1, n1); PUSHINT(qr.rem); PUSHINT(qr.quot); return; } /************************************************************************** s m S l a s h R e m ** s-m-slash-rem CORE ( d1 n1 -- n2 n3 ) ** Divide d1 by n1, giving the symmetric quotient n3 and the remainder n2. ** Input and output stack arguments are signed. An ambiguous condition ** exists if n1 is zero or if the quotient lies outside the range of a ** single-cell signed integer. **************************************************************************/ static void smSlashRem(FICL_VM *pVM) { DPINT d1; FICL_INT n1; INTQR qr; #if FICL_ROBUST > 1 vmCheckStack(pVM,3,2); #endif n1 = POPINT(); d1 = i64Pop(pVM->pStack); qr = m64SymmetricDivI(d1, n1); PUSHINT(qr.rem); PUSHINT(qr.quot); return; } static void ficlMod(FICL_VM *pVM) { DPINT d1; FICL_INT n1; INTQR qr; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,1); #endif n1 = POPINT(); d1.lo = POPINT(); i64Extend(d1); qr = m64SymmetricDivI(d1, n1); PUSHINT(qr.rem); return; } /************************************************************************** u m S l a s h M o d ** u-m-slash-mod CORE ( ud u1 -- u2 u3 ) ** Divide ud by u1, giving the quotient u3 and the remainder u2. ** All values and arithmetic are unsigned. An ambiguous condition ** exists if u1 is zero or if the quotient lies outside the range of a ** single-cell unsigned integer. *************************************************************************/ static void umSlashMod(FICL_VM *pVM) { DPUNS ud; FICL_UNS u1; UNSQR qr; u1 = stackPopUNS(pVM->pStack); ud = u64Pop(pVM->pStack); qr = ficlLongDiv(ud, u1); PUSHUNS(qr.rem); PUSHUNS(qr.quot); return; } /************************************************************************** l s h i f t ** l-shift CORE ( x1 u -- x2 ) ** Perform a logical left shift of u bit-places on x1, giving x2. ** Put zeroes into the least significant bits vacated by the shift. ** An ambiguous condition exists if u is greater than or equal to the ** number of bits in a cell. ** ** r-shift CORE ( x1 u -- x2 ) ** Perform a logical right shift of u bit-places on x1, giving x2. ** Put zeroes into the most significant bits vacated by the shift. An ** ambiguous condition exists if u is greater than or equal to the ** number of bits in a cell. **************************************************************************/ static void lshift(FICL_VM *pVM) { FICL_UNS nBits; FICL_UNS x1; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,1); #endif nBits = POPUNS(); x1 = POPUNS(); PUSHUNS(x1 << nBits); return; } static void rshift(FICL_VM *pVM) { FICL_UNS nBits; FICL_UNS x1; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,1); #endif nBits = POPUNS(); x1 = POPUNS(); PUSHUNS(x1 >> nBits); return; } /************************************************************************** m S t a r ** m-star CORE ( n1 n2 -- d ) ** d is the signed product of n1 times n2. **************************************************************************/ static void mStar(FICL_VM *pVM) { FICL_INT n2; FICL_INT n1; DPINT d; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,2); #endif n2 = POPINT(); n1 = POPINT(); d = m64MulI(n1, n2); i64Push(pVM->pStack, d); return; } static void umStar(FICL_VM *pVM) { FICL_UNS u2; FICL_UNS u1; DPUNS ud; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,2); #endif u2 = POPUNS(); u1 = POPUNS(); ud = ficlLongMul(u1, u2); u64Push(pVM->pStack, ud); return; } /************************************************************************** m a x & m i n ** **************************************************************************/ static void ficlMax(FICL_VM *pVM) { FICL_INT n2; FICL_INT n1; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,1); #endif n2 = POPINT(); n1 = POPINT(); PUSHINT((n1 > n2) ? n1 : n2); return; } static void ficlMin(FICL_VM *pVM) { FICL_INT n2; FICL_INT n1; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,1); #endif n2 = POPINT(); n1 = POPINT(); PUSHINT((n1 < n2) ? n1 : n2); return; } /************************************************************************** m o v e ** CORE ( addr1 addr2 u -- ) ** If u is greater than zero, copy the contents of u consecutive address ** units at addr1 to the u consecutive address units at addr2. After MOVE ** completes, the u consecutive address units at addr2 contain exactly ** what the u consecutive address units at addr1 contained before the move. ** NOTE! This implementation assumes that a char is the same size as ** an address unit. **************************************************************************/ static void move(FICL_VM *pVM) { FICL_UNS u; char *addr2; char *addr1; #if FICL_ROBUST > 1 vmCheckStack(pVM,3,0); #endif u = POPUNS(); addr2 = POPPTR(); addr1 = POPPTR(); if (u == 0) return; /* ** Do the copy carefully, so as to be ** correct even if the two ranges overlap */ if (addr1 >= addr2) { for (; u > 0; u--) *addr2++ = *addr1++; } else { addr2 += u-1; addr1 += u-1; for (; u > 0; u--) *addr2-- = *addr1--; } return; } /************************************************************************** r e c u r s e ** **************************************************************************/ static void recurseCoIm(FICL_VM *pVM) { FICL_DICT *pDict = vmGetDict(pVM); IGNORE(pVM); dictAppendCell(pDict, LVALUEtoCELL(pDict->smudge)); return; } /************************************************************************** s t o d ** s-to-d CORE ( n -- d ) ** Convert the number n to the double-cell number d with the same ** numerical value. **************************************************************************/ static void sToD(FICL_VM *pVM) { FICL_INT s; #if FICL_ROBUST > 1 vmCheckStack(pVM,1,2); #endif s = POPINT(); /* sign extend to 64 bits.. */ PUSHINT(s); PUSHINT((s < 0) ? -1 : 0); return; } /************************************************************************** s o u r c e ** CORE ( -- c-addr u ) ** c-addr is the address of, and u is the number of characters in, the ** input buffer. **************************************************************************/ static void source(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM,0,2); #endif PUSHPTR(pVM->tib.cp); PUSHINT(vmGetInBufLen(pVM)); return; } /************************************************************************** v e r s i o n ** non-standard... **************************************************************************/ static void ficlVersion(FICL_VM *pVM) { vmTextOut(pVM, "ficl Version " FICL_VER, 1); return; } /************************************************************************** t o I n ** to-in CORE **************************************************************************/ static void toIn(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM,0,1); #endif PUSHPTR(&pVM->tib.index); return; } /************************************************************************** c o l o n N o N a m e ** CORE EXT ( C: -- colon-sys ) ( S: -- xt ) ** Create an unnamed colon definition and push its address. ** Change state to compile. **************************************************************************/ static void colonNoName(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); FICL_WORD *pFW; STRINGINFO si; SI_SETLEN(si, 0); SI_SETPTR(si, NULL); pVM->state = COMPILE; pFW = dictAppendWord2(dp, si, colonParen, FW_DEFAULT | FW_SMUDGE); PUSHPTR(pFW); markControlTag(pVM, colonTag); return; } /************************************************************************** u s e r V a r i a b l e ** user ( u -- ) "name" ** Get a name from the input stream and create a user variable ** with the name and the index supplied. The run-time effect ** of a user variable is to push the address of the indexed cell ** in the running vm's user array. ** ** User variables are vm local cells. Each vm has an array of ** FICL_USER_CELLS of them when FICL_WANT_USER is nonzero. ** Ficl's user facility is implemented with two primitives, ** "user" and "(user)", a variable ("nUser") (in softcore.c) that ** holds the index of the next free user cell, and a redefinition ** (also in softcore) of "user" that defines a user word and increments ** nUser. **************************************************************************/ #if FICL_WANT_USER static void userParen(FICL_VM *pVM) { FICL_INT i = pVM->runningWord->param[0].i; PUSHPTR(&pVM->user[i]); return; } static void userVariable(FICL_VM *pVM) { FICL_DICT *dp = vmGetDict(pVM); STRINGINFO si = vmGetWord(pVM); CELL c; c = stackPop(pVM->pStack); if (c.i >= FICL_USER_CELLS) { vmThrowErr(pVM, "Error - out of user space"); } dictAppendWord2(dp, si, userParen, FW_DEFAULT); dictAppendCell(dp, c); return; } #endif /************************************************************************** t o V a l u e ** CORE EXT ** Interpretation: ( x "name" -- ) ** Skip leading spaces and parse name delimited by a space. Store x in ** name. An ambiguous condition exists if name was not defined by VALUE. ** NOTE: In ficl, VALUE is an alias of CONSTANT **************************************************************************/ static void toValue(FICL_VM *pVM) { STRINGINFO si = vmGetWord(pVM); FICL_DICT *dp = vmGetDict(pVM); FICL_WORD *pFW; #if FICL_WANT_LOCALS if ((pVM->pSys->nLocals > 0) && (pVM->state == COMPILE)) { FICL_DICT *pLoc = ficlGetLoc(pVM->pSys); pFW = dictLookup(pLoc, si); if (pFW && (pFW->code == doLocalIm)) { dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pToLocalParen)); dictAppendCell(dp, LVALUEtoCELL(pFW->param[0])); return; } else if (pFW && pFW->code == do2LocalIm) { dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pTo2LocalParen)); dictAppendCell(dp, LVALUEtoCELL(pFW->param[0])); return; } } #endif assert(pVM->pSys->pStore); pFW = dictLookup(dp, si); if (!pFW) { int i = SI_COUNT(si); vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si)); } if (pVM->state == INTERPRET) pFW->param[0] = stackPop(pVM->pStack); else /* compile code to store to word's param */ { PUSHPTR(&pFW->param[0]); literalIm(pVM); dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStore)); } return; } #if FICL_WANT_LOCALS /************************************************************************** l i n k P a r e n ** ( -- ) ** Link a frame on the return stack, reserving nCells of space for ** locals - the value of nCells is the next cell in the instruction ** stream. **************************************************************************/ static void linkParen(FICL_VM *pVM) { FICL_INT nLink = *(FICL_INT *)(pVM->ip); vmBranchRelative(pVM, 1); stackLink(pVM->rStack, nLink); return; } static void unlinkParen(FICL_VM *pVM) { stackUnlink(pVM->rStack); return; } /************************************************************************** d o L o c a l I m ** Immediate - cfa of a local while compiling - when executed, compiles ** code to fetch the value of a local given the local's index in the ** word's pfa **************************************************************************/ static void getLocalParen(FICL_VM *pVM) { FICL_INT nLocal = *(FICL_INT *)(pVM->ip++); stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]); return; } static void toLocalParen(FICL_VM *pVM) { FICL_INT nLocal = *(FICL_INT *)(pVM->ip++); pVM->rStack->pFrame[nLocal] = stackPop(pVM->pStack); return; } static void getLocal0(FICL_VM *pVM) { stackPush(pVM->pStack, pVM->rStack->pFrame[0]); return; } static void toLocal0(FICL_VM *pVM) { pVM->rStack->pFrame[0] = stackPop(pVM->pStack); return; } static void getLocal1(FICL_VM *pVM) { stackPush(pVM->pStack, pVM->rStack->pFrame[1]); return; } static void toLocal1(FICL_VM *pVM) { pVM->rStack->pFrame[1] = stackPop(pVM->pStack); return; } /* ** Each local is recorded in a private locals dictionary as a ** word that does doLocalIm at runtime. DoLocalIm compiles code ** into the client definition to fetch the value of the ** corresponding local variable from the return stack. ** The private dictionary gets initialized at the end of each block ** that uses locals (in ; and does> for example). */ static void doLocalIm(FICL_VM *pVM) { FICL_DICT *pDict = vmGetDict(pVM); FICL_INT nLocal = pVM->runningWord->param[0].i; if (pVM->state == INTERPRET) { stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]); } else { if (nLocal == 0) { dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocal0)); } else if (nLocal == 1) { dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocal1)); } else { dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocalParen)); dictAppendCell(pDict, LVALUEtoCELL(nLocal)); } } return; } /************************************************************************** l o c a l P a r e n ** paren-local-paren LOCAL ** Interpretation: Interpretation semantics for this word are undefined. ** Execution: ( c-addr u -- ) ** When executed during compilation, (LOCAL) passes a message to the ** system that has one of two meanings. If u is non-zero, ** the message identifies a new local whose definition name is given by ** the string of characters identified by c-addr u. If u is zero, ** the message is last local and c-addr has no significance. ** ** The result of executing (LOCAL) during compilation of a definition is ** to create a set of named local identifiers, each of which is ** a definition name, that only have execution semantics within the scope ** of that definition's source. ** ** local Execution: ( -- x ) ** ** Push the local's value, x, onto the stack. The local's value is ** initialized as described in 13.3.3 Processing locals and may be ** changed by preceding the local's name with TO. An ambiguous condition ** exists when local is executed while in interpretation state. **************************************************************************/ static void localParen(FICL_VM *pVM) { FICL_DICT *pDict; STRINGINFO si; #if FICL_ROBUST > 1 vmCheckStack(pVM,2,0); #endif pDict = vmGetDict(pVM); SI_SETLEN(si, POPUNS()); SI_SETPTR(si, (char *)POPPTR()); if (SI_COUNT(si) > 0) { /* add a local to the **locals** dict and update nLocals */ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys); if (pVM->pSys->nLocals >= FICL_MAX_LOCALS) { vmThrowErr(pVM, "Error: out of local space"); } dictAppendWord2(pLoc, si, doLocalIm, FW_COMPIMMED); dictAppendCell(pLoc, LVALUEtoCELL(pVM->pSys->nLocals)); if (pVM->pSys->nLocals == 0) { /* compile code to create a local stack frame */ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pLinkParen)); /* save location in dictionary for #locals */ pVM->pSys->pMarkLocals = pDict->here; dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals)); /* compile code to initialize first local */ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocal0)); } else if (pVM->pSys->nLocals == 1) { dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocal1)); } else { dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocalParen)); dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals)); } (pVM->pSys->nLocals)++; } else if (pVM->pSys->nLocals > 0) { /* write nLocals to (link) param area in dictionary */ *(FICL_INT *)(pVM->pSys->pMarkLocals) = pVM->pSys->nLocals; } return; } static void get2LocalParen(FICL_VM *pVM) { FICL_INT nLocal = *(FICL_INT *)(pVM->ip++); stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]); stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal+1]); return; } static void do2LocalIm(FICL_VM *pVM) { FICL_DICT *pDict = vmGetDict(pVM); FICL_INT nLocal = pVM->runningWord->param[0].i; if (pVM->state == INTERPRET) { stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]); stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal+1]); } else { dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGet2LocalParen)); dictAppendCell(pDict, LVALUEtoCELL(nLocal)); } return; } static void to2LocalParen(FICL_VM *pVM) { FICL_INT nLocal = *(FICL_INT *)(pVM->ip++); pVM->rStack->pFrame[nLocal+1] = stackPop(pVM->pStack); pVM->rStack->pFrame[nLocal] = stackPop(pVM->pStack); return; } static void twoLocalParen(FICL_VM *pVM) { FICL_DICT *pDict = vmGetDict(pVM); STRINGINFO si; SI_SETLEN(si, stackPopUNS(pVM->pStack)); SI_SETPTR(si, (char *)stackPopPtr(pVM->pStack)); if (SI_COUNT(si) > 0) { /* add a local to the **locals** dict and update nLocals */ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys); if (pVM->pSys->nLocals >= FICL_MAX_LOCALS) { vmThrowErr(pVM, "Error: out of local space"); } dictAppendWord2(pLoc, si, do2LocalIm, FW_COMPIMMED); dictAppendCell(pLoc, LVALUEtoCELL(pVM->pSys->nLocals)); if (pVM->pSys->nLocals == 0) { /* compile code to create a local stack frame */ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pLinkParen)); /* save location in dictionary for #locals */ pVM->pSys->pMarkLocals = pDict->here; dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals)); } dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pTo2LocalParen)); dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals)); pVM->pSys->nLocals += 2; } else if (pVM->pSys->nLocals > 0) { /* write nLocals to (link) param area in dictionary */ *(FICL_INT *)(pVM->pSys->pMarkLocals) = pVM->pSys->nLocals; } return; } #endif /************************************************************************** c o m p a r e ** STRING ( c-addr1 u1 c-addr2 u2 -- n ) ** Compare the string specified by c-addr1 u1 to the string specified by ** c-addr2 u2. The strings are compared, beginning at the given addresses, ** character by character, up to the length of the shorter string or until a ** difference is found. If the two strings are identical, n is zero. If the two ** strings are identical up to the length of the shorter string, n is minus-one ** (-1) if u1 is less than u2 and one (1) otherwise. If the two strings are not ** identical up to the length of the shorter string, n is minus-one (-1) if the ** first non-matching character in the string specified by c-addr1 u1 has a ** lesser numeric value than the corresponding character in the string specified ** by c-addr2 u2 and one (1) otherwise. **************************************************************************/ static void compareInternal(FICL_VM *pVM, int caseInsensitive) { char *cp1, *cp2; FICL_UNS u1, u2, uMin; int n = 0; vmCheckStack(pVM, 4, 1); u2 = stackPopUNS(pVM->pStack); cp2 = (char *)stackPopPtr(pVM->pStack); u1 = stackPopUNS(pVM->pStack); cp1 = (char *)stackPopPtr(pVM->pStack); uMin = (u1 < u2)? u1 : u2; for ( ; (uMin > 0) && (n == 0); uMin--) { char c1 = *cp1++; char c2 = *cp2++; if (caseInsensitive) { c1 = (char)tolower(c1); c2 = (char)tolower(c2); } n = (int)(c1 - c2); } if (n == 0) n = (int)(u1 - u2); if (n < 0) n = -1; else if (n > 0) n = 1; PUSHINT(n); return; } static void compareString(FICL_VM *pVM) { compareInternal(pVM, FALSE); } static void compareStringInsensitive(FICL_VM *pVM) { compareInternal(pVM, TRUE); } /************************************************************************** p a d ** CORE EXT ( -- c-addr ) ** c-addr is the address of a transient region that can be used to hold ** data for intermediate processing. **************************************************************************/ static void pad(FICL_VM *pVM) { stackPushPtr(pVM->pStack, pVM->pad); } /************************************************************************** s o u r c e - i d ** CORE EXT, FILE ( -- 0 | -1 | fileid ) ** Identifies the input source as follows: ** ** SOURCE-ID Input source ** --------- ------------ ** fileid Text file fileid ** -1 String (via EVALUATE) ** 0 User input device **************************************************************************/ static void sourceid(FICL_VM *pVM) { PUSHINT(pVM->sourceID.i); return; } /************************************************************************** r e f i l l ** CORE EXT ( -- flag ) ** Attempt to fill the input buffer from the input source, returning a true ** flag if successful. ** When the input source is the user input device, attempt to receive input ** into the terminal input buffer. If successful, make the result the input ** buffer, set >IN to zero, and return true. Receipt of a line containing no ** characters is considered successful. If there is no input available from ** the current input source, return false. ** When the input source is a string from EVALUATE, return false and ** perform no other action. **************************************************************************/ static void refill(FICL_VM *pVM) { FICL_INT ret = (pVM->sourceID.i == -1) ? FICL_FALSE : FICL_TRUE; if (ret && (pVM->fRestart == 0)) vmThrow(pVM, VM_RESTART); PUSHINT(ret); return; } /************************************************************************** freebsd exception handling words ** Catch, from ANS Forth standard. Installs a safety net, then EXECUTE ** the word in ToS. If an exception happens, restore the state to what ** it was before, and pushes the exception value on the stack. If not, ** push zero. ** ** Notice that Catch implements an inner interpreter. This is ugly, ** but given how ficl works, it cannot be helped. The problem is that ** colon definitions will be executed *after* the function returns, ** while "code" definitions will be executed immediately. I considered ** other solutions to this problem, but all of them shared the same ** basic problem (with added disadvantages): if ficl ever changes it's ** inner thread modus operandi, one would have to fix this word. ** ** More comments can be found throughout catch's code. ** ** Daniel C. Sobral Jan 09/1999 ** sadler may 2000 -- revised to follow ficl.c:ficlExecXT. **************************************************************************/ static void ficlCatch(FICL_VM *pVM) { int except; jmp_buf vmState; FICL_VM VM; FICL_STACK pStack; FICL_STACK rStack; FICL_WORD *pFW; assert(pVM); assert(pVM->pSys->pExitInner); /* ** Get xt. ** We need this *before* we save the stack pointer, or ** we'll have to pop one element out of the stack after ** an exception. I prefer to get done with it up front. :-) */ #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif pFW = stackPopPtr(pVM->pStack); /* ** Save vm's state -- a catch will not back out environmental ** changes. ** ** We are *not* saving dictionary state, since it is ** global instead of per vm, and we are not saving ** stack contents, since we are not required to (and, ** thus, it would be useless). We save pVM, and pVM ** "stacks" (a structure containing general information ** about it, including the current stack pointer). */ memcpy((void*)&VM, (void*)pVM, sizeof(FICL_VM)); memcpy((void*)&pStack, (void*)pVM->pStack, sizeof(FICL_STACK)); memcpy((void*)&rStack, (void*)pVM->rStack, sizeof(FICL_STACK)); /* ** Give pVM a jmp_buf */ pVM->pState = &vmState; /* ** Safety net */ except = setjmp(vmState); switch (except) { /* ** Setup condition - push poison pill so that the VM throws ** VM_INNEREXIT if the XT terminates normally, then execute ** the XT */ case 0: vmPushIP(pVM, &(pVM->pSys->pExitInner)); /* Open mouth, insert emetic */ vmExecute(pVM, pFW); vmInnerLoop(pVM); break; /* ** Normal exit from XT - lose the poison pill, ** restore old setjmp vector and push a zero. */ case VM_INNEREXIT: vmPopIP(pVM); /* Gack - hurl poison pill */ pVM->pState = VM.pState; /* Restore just the setjmp vector */ PUSHINT(0); /* Push 0 -- everything is ok */ break; /* ** Some other exception got thrown - restore pre-existing VM state ** and push the exception code */ default: /* Restore vm's state */ memcpy((void*)pVM, (void*)&VM, sizeof(FICL_VM)); memcpy((void*)pVM->pStack, (void*)&pStack, sizeof(FICL_STACK)); memcpy((void*)pVM->rStack, (void*)&rStack, sizeof(FICL_STACK)); PUSHINT(except);/* Push error */ break; } } /************************************************************************** ** t h r o w ** EXCEPTION ** Throw -- From ANS Forth standard. ** ** Throw takes the ToS and, if that's different from zero, ** returns to the last executed catch context. Further throws will ** unstack previously executed "catches", in LIFO mode. ** ** Daniel C. Sobral Jan 09/1999 **************************************************************************/ static void ficlThrow(FICL_VM *pVM) { int except; except = stackPopINT(pVM->pStack); if (except) vmThrow(pVM, except); } /************************************************************************** ** a l l o c a t e ** MEMORY **************************************************************************/ static void ansAllocate(FICL_VM *pVM) { size_t size; void *p; size = stackPopINT(pVM->pStack); p = ficlMalloc(size); PUSHPTR(p); if (p) PUSHINT(0); else PUSHINT(1); } /************************************************************************** ** f r e e ** MEMORY **************************************************************************/ static void ansFree(FICL_VM *pVM) { void *p; p = stackPopPtr(pVM->pStack); ficlFree(p); PUSHINT(0); } /************************************************************************** ** r e s i z e ** MEMORY **************************************************************************/ static void ansResize(FICL_VM *pVM) { size_t size; void *new, *old; size = stackPopINT(pVM->pStack); old = stackPopPtr(pVM->pStack); new = ficlRealloc(old, size); if (new) { PUSHPTR(new); PUSHINT(0); } else { PUSHPTR(old); PUSHINT(1); } } /************************************************************************** ** e x i t - i n n e r ** Signals execXT that an inner loop has completed **************************************************************************/ static void ficlExitInner(FICL_VM *pVM) { vmThrow(pVM, VM_INNEREXIT); } /************************************************************************** d n e g a t e ** DOUBLE ( d1 -- d2 ) ** d2 is the negation of d1. **************************************************************************/ static void dnegate(FICL_VM *pVM) { DPINT i = i64Pop(pVM->pStack); i = m64Negate(i); i64Push(pVM->pStack, i); return; } #if 0 /************************************************************************** ** **************************************************************************/ static void funcname(FICL_VM *pVM) { IGNORE(pVM); return; } #endif /************************************************************************** f i c l W o r d C l a s s i f y ** This public function helps to classify word types for SEE ** and the deugger in tools.c. Given a pointer to a word, it returns ** a member of WOR **************************************************************************/ WORDKIND ficlWordClassify(FICL_WORD *pFW) { typedef struct { WORDKIND kind; FICL_CODE code; } CODEtoKIND; static CODEtoKIND codeMap[] = { {BRANCH, branchParen}, {COLON, colonParen}, {CONSTANT, constantParen}, {CREATE, createParen}, {DO, doParen}, {DOES, doDoes}, {IF, branch0}, {LITERAL, literalParen}, {LOOP, loopParen}, {OF, ofParen}, {PLOOP, plusLoopParen}, {QDO, qDoParen}, {CSTRINGLIT, cstringLit}, {STRINGLIT, stringLit}, #if FICL_WANT_USER {USER, userParen}, #endif {VARIABLE, variableParen}, }; #define nMAP (sizeof(codeMap) / sizeof(CODEtoKIND)) FICL_CODE code = pFW->code; int i; for (i=0; i < nMAP; i++) { if (codeMap[i].code == code) return codeMap[i].kind; } return PRIMITIVE; } #ifdef TESTMAIN /************************************************************************** ** r a n d o m ** FICL-specific **************************************************************************/ static void ficlRandom(FICL_VM *pVM) { - PUSHINT(rand()); + PUSHUNS(random()); } /************************************************************************** ** s e e d - r a n d o m ** FICL-specific **************************************************************************/ static void ficlSeedRandom(FICL_VM *pVM) { - srand(POPINT()); + srandom(POPUNS()); } #endif /************************************************************************** f i c l C o m p i l e C o r e ** Builds the primitive wordset and the environment-query namespace. **************************************************************************/ void ficlCompileCore(FICL_SYSTEM *pSys) { FICL_DICT *dp = pSys->dp; assert (dp); /* ** CORE word set ** see softcore.c for definitions of: abs bl space spaces abort" */ pSys->pStore = dictAppendWord(dp, "!", store, FW_DEFAULT); dictAppendWord(dp, "#", numberSign, FW_DEFAULT); dictAppendWord(dp, "#>", numberSignGreater,FW_DEFAULT); dictAppendWord(dp, "#s", numberSignS, FW_DEFAULT); dictAppendWord(dp, "\'", ficlTick, FW_DEFAULT); dictAppendWord(dp, "(", commentHang, FW_IMMEDIATE); dictAppendWord(dp, "*", mul, FW_DEFAULT); dictAppendWord(dp, "*/", mulDiv, FW_DEFAULT); dictAppendWord(dp, "*/mod", mulDivRem, FW_DEFAULT); dictAppendWord(dp, "+", add, FW_DEFAULT); dictAppendWord(dp, "+!", plusStore, FW_DEFAULT); dictAppendWord(dp, "+loop", plusLoopCoIm, FW_COMPIMMED); dictAppendWord(dp, ",", comma, FW_DEFAULT); dictAppendWord(dp, "-", sub, FW_DEFAULT); dictAppendWord(dp, ".", displayCell, FW_DEFAULT); dictAppendWord(dp, ".\"", dotQuoteCoIm, FW_COMPIMMED); dictAppendWord(dp, "/", ficlDiv, FW_DEFAULT); dictAppendWord(dp, "/mod", slashMod, FW_DEFAULT); dictAppendWord(dp, "0<", zeroLess, FW_DEFAULT); dictAppendWord(dp, "0=", zeroEquals, FW_DEFAULT); dictAppendWord(dp, "1+", onePlus, FW_DEFAULT); dictAppendWord(dp, "1-", oneMinus, FW_DEFAULT); dictAppendWord(dp, "2!", twoStore, FW_DEFAULT); dictAppendWord(dp, "2*", twoMul, FW_DEFAULT); dictAppendWord(dp, "2/", twoDiv, FW_DEFAULT); dictAppendWord(dp, "2@", twoFetch, FW_DEFAULT); dictAppendWord(dp, "2drop", twoDrop, FW_DEFAULT); dictAppendWord(dp, "2dup", twoDup, FW_DEFAULT); dictAppendWord(dp, "2over", twoOver, FW_DEFAULT); dictAppendWord(dp, "2swap", twoSwap, FW_DEFAULT); dictAppendWord(dp, ":", colon, FW_DEFAULT); dictAppendWord(dp, ";", semicolonCoIm, FW_COMPIMMED); dictAppendWord(dp, "<", isLess, FW_DEFAULT); dictAppendWord(dp, "<#", lessNumberSign, FW_DEFAULT); dictAppendWord(dp, "=", isEqual, FW_DEFAULT); dictAppendWord(dp, ">", isGreater, FW_DEFAULT); dictAppendWord(dp, ">body", toBody, FW_DEFAULT); dictAppendWord(dp, ">in", toIn, FW_DEFAULT); dictAppendWord(dp, ">number", toNumber, FW_DEFAULT); dictAppendWord(dp, ">r", toRStack, FW_COMPILE); dictAppendWord(dp, "?dup", questionDup, FW_DEFAULT); dictAppendWord(dp, "@", fetch, FW_DEFAULT); dictAppendWord(dp, "abort", ficlAbort, FW_DEFAULT); dictAppendWord(dp, "accept", accept, FW_DEFAULT); dictAppendWord(dp, "align", align, FW_DEFAULT); dictAppendWord(dp, "aligned", aligned, FW_DEFAULT); dictAppendWord(dp, "allot", allot, FW_DEFAULT); dictAppendWord(dp, "and", bitwiseAnd, FW_DEFAULT); dictAppendWord(dp, "base", base, FW_DEFAULT); dictAppendWord(dp, "begin", beginCoIm, FW_COMPIMMED); dictAppendWord(dp, "c!", cStore, FW_DEFAULT); dictAppendWord(dp, "c,", cComma, FW_DEFAULT); dictAppendWord(dp, "c@", cFetch, FW_DEFAULT); dictAppendWord(dp, "case", caseCoIm, FW_COMPIMMED); dictAppendWord(dp, "cell+", cellPlus, FW_DEFAULT); dictAppendWord(dp, "cells", cells, FW_DEFAULT); dictAppendWord(dp, "char", ficlChar, FW_DEFAULT); dictAppendWord(dp, "char+", charPlus, FW_DEFAULT); dictAppendWord(dp, "chars", ficlChars, FW_DEFAULT); dictAppendWord(dp, "constant", constant, FW_DEFAULT); dictAppendWord(dp, "count", count, FW_DEFAULT); dictAppendWord(dp, "cr", cr, FW_DEFAULT); dictAppendWord(dp, "create", create, FW_DEFAULT); dictAppendWord(dp, "decimal", decimal, FW_DEFAULT); dictAppendWord(dp, "depth", depth, FW_DEFAULT); dictAppendWord(dp, "do", doCoIm, FW_COMPIMMED); dictAppendWord(dp, "does>", doesCoIm, FW_COMPIMMED); pSys->pDrop = dictAppendWord(dp, "drop", drop, FW_DEFAULT); dictAppendWord(dp, "dup", dup, FW_DEFAULT); dictAppendWord(dp, "else", elseCoIm, FW_COMPIMMED); dictAppendWord(dp, "emit", emit, FW_DEFAULT); dictAppendWord(dp, "endcase", endcaseCoIm, FW_COMPIMMED); dictAppendWord(dp, "endof", endofCoIm, FW_COMPIMMED); dictAppendWord(dp, "environment?", environmentQ,FW_DEFAULT); dictAppendWord(dp, "evaluate", evaluate, FW_DEFAULT); dictAppendWord(dp, "execute", execute, FW_DEFAULT); dictAppendWord(dp, "exit", exitCoIm, FW_COMPIMMED); dictAppendWord(dp, "fallthrough",fallthroughCoIm,FW_COMPIMMED); dictAppendWord(dp, "fill", fill, FW_DEFAULT); dictAppendWord(dp, "find", cFind, FW_DEFAULT); dictAppendWord(dp, "fm/mod", fmSlashMod, FW_DEFAULT); dictAppendWord(dp, "here", here, FW_DEFAULT); dictAppendWord(dp, "hold", hold, FW_DEFAULT); dictAppendWord(dp, "i", loopICo, FW_COMPILE); dictAppendWord(dp, "if", ifCoIm, FW_COMPIMMED); dictAppendWord(dp, "immediate", immediate, FW_DEFAULT); dictAppendWord(dp, "invert", bitwiseNot, FW_DEFAULT); dictAppendWord(dp, "j", loopJCo, FW_COMPILE); dictAppendWord(dp, "k", loopKCo, FW_COMPILE); dictAppendWord(dp, "leave", leaveCo, FW_COMPILE); dictAppendWord(dp, "literal", literalIm, FW_IMMEDIATE); dictAppendWord(dp, "loop", loopCoIm, FW_COMPIMMED); dictAppendWord(dp, "lshift", lshift, FW_DEFAULT); dictAppendWord(dp, "m*", mStar, FW_DEFAULT); dictAppendWord(dp, "max", ficlMax, FW_DEFAULT); dictAppendWord(dp, "min", ficlMin, FW_DEFAULT); dictAppendWord(dp, "mod", ficlMod, FW_DEFAULT); dictAppendWord(dp, "move", move, FW_DEFAULT); dictAppendWord(dp, "negate", negate, FW_DEFAULT); dictAppendWord(dp, "of", ofCoIm, FW_COMPIMMED); dictAppendWord(dp, "or", bitwiseOr, FW_DEFAULT); dictAppendWord(dp, "over", over, FW_DEFAULT); dictAppendWord(dp, "postpone", postponeCoIm, FW_COMPIMMED); dictAppendWord(dp, "quit", quit, FW_DEFAULT); dictAppendWord(dp, "r>", fromRStack, FW_COMPILE); dictAppendWord(dp, "r@", fetchRStack, FW_COMPILE); dictAppendWord(dp, "recurse", recurseCoIm, FW_COMPIMMED); dictAppendWord(dp, "repeat", repeatCoIm, FW_COMPIMMED); dictAppendWord(dp, "rot", rot, FW_DEFAULT); dictAppendWord(dp, "rshift", rshift, FW_DEFAULT); dictAppendWord(dp, "s\"", stringQuoteIm, FW_IMMEDIATE); dictAppendWord(dp, "s>d", sToD, FW_DEFAULT); dictAppendWord(dp, "sign", sign, FW_DEFAULT); dictAppendWord(dp, "sm/rem", smSlashRem, FW_DEFAULT); dictAppendWord(dp, "source", source, FW_DEFAULT); dictAppendWord(dp, "state", state, FW_DEFAULT); dictAppendWord(dp, "swap", swap, FW_DEFAULT); dictAppendWord(dp, "then", endifCoIm, FW_COMPIMMED); dictAppendWord(dp, "type", type, FW_DEFAULT); dictAppendWord(dp, "u.", uDot, FW_DEFAULT); dictAppendWord(dp, "u<", uIsLess, FW_DEFAULT); dictAppendWord(dp, "um*", umStar, FW_DEFAULT); dictAppendWord(dp, "um/mod", umSlashMod, FW_DEFAULT); dictAppendWord(dp, "unloop", unloopCo, FW_COMPILE); dictAppendWord(dp, "until", untilCoIm, FW_COMPIMMED); dictAppendWord(dp, "variable", variable, FW_DEFAULT); dictAppendWord(dp, "while", whileCoIm, FW_COMPIMMED); dictAppendWord(dp, "word", ficlWord, FW_DEFAULT); dictAppendWord(dp, "xor", bitwiseXor, FW_DEFAULT); dictAppendWord(dp, "[", lbracketCoIm, FW_COMPIMMED); dictAppendWord(dp, "[\']", bracketTickCoIm,FW_COMPIMMED); dictAppendWord(dp, "[char]", charCoIm, FW_COMPIMMED); dictAppendWord(dp, "]", rbracket, FW_DEFAULT); /* ** CORE EXT word set... ** see softcore.fr for other definitions */ /* "#tib" */ dictAppendWord(dp, ".(", dotParen, FW_IMMEDIATE); /* ".r" */ dictAppendWord(dp, "0>", zeroGreater, FW_DEFAULT); dictAppendWord(dp, "2>r", twoToR, FW_COMPILE); dictAppendWord(dp, "2r>", twoRFrom, FW_COMPILE); dictAppendWord(dp, "2r@", twoRFetch, FW_COMPILE); dictAppendWord(dp, ":noname", colonNoName, FW_DEFAULT); dictAppendWord(dp, "?do", qDoCoIm, FW_COMPIMMED); dictAppendWord(dp, "again", againCoIm, FW_COMPIMMED); dictAppendWord(dp, "c\"", cstringQuoteIm, FW_IMMEDIATE); dictAppendWord(dp, "hex", hex, FW_DEFAULT); dictAppendWord(dp, "pad", pad, FW_DEFAULT); dictAppendWord(dp, "parse", parse, FW_DEFAULT); dictAppendWord(dp, "pick", pick, FW_DEFAULT); /* query restore-input save-input tib u.r u> unused [compile] */ dictAppendWord(dp, "roll", roll, FW_DEFAULT); dictAppendWord(dp, "refill", refill, FW_DEFAULT); dictAppendWord(dp, "source-id", sourceid, FW_DEFAULT); dictAppendWord(dp, "to", toValue, FW_IMMEDIATE); dictAppendWord(dp, "value", constant, FW_DEFAULT); dictAppendWord(dp, "\\", commentLine, FW_IMMEDIATE); /* ** Set CORE environment query values */ ficlSetEnv(pSys, "/counted-string", FICL_STRING_MAX); ficlSetEnv(pSys, "/hold", nPAD); ficlSetEnv(pSys, "/pad", nPAD); ficlSetEnv(pSys, "address-unit-bits", 8); ficlSetEnv(pSys, "core", FICL_TRUE); ficlSetEnv(pSys, "core-ext", FICL_FALSE); ficlSetEnv(pSys, "floored", FICL_FALSE); ficlSetEnv(pSys, "max-char", UCHAR_MAX); ficlSetEnvD(pSys,"max-d", 0x7fffffff, 0xffffffff); ficlSetEnv(pSys, "max-n", 0x7fffffff); ficlSetEnv(pSys, "max-u", 0xffffffff); ficlSetEnvD(pSys,"max-ud", 0xffffffff, 0xffffffff); ficlSetEnv(pSys, "return-stack-cells",FICL_DEFAULT_STACK); ficlSetEnv(pSys, "stack-cells", FICL_DEFAULT_STACK); /* ** DOUBLE word set (partial) */ dictAppendWord(dp, "2constant", twoConstant, FW_IMMEDIATE); dictAppendWord(dp, "2literal", twoLiteralIm, FW_IMMEDIATE); dictAppendWord(dp, "2variable", twoVariable, FW_IMMEDIATE); dictAppendWord(dp, "dnegate", dnegate, FW_DEFAULT); /* ** EXCEPTION word set */ dictAppendWord(dp, "catch", ficlCatch, FW_DEFAULT); dictAppendWord(dp, "throw", ficlThrow, FW_DEFAULT); ficlSetEnv(pSys, "exception", FICL_TRUE); ficlSetEnv(pSys, "exception-ext", FICL_TRUE); /* ** LOCAL and LOCAL EXT ** see softcore.c for implementation of locals| */ #if FICL_WANT_LOCALS pSys->pLinkParen = dictAppendWord(dp, "(link)", linkParen, FW_COMPILE); pSys->pUnLinkParen = dictAppendWord(dp, "(unlink)", unlinkParen, FW_COMPILE); dictAppendWord(dp, "doLocal", doLocalIm, FW_COMPIMMED); pSys->pGetLocalParen = dictAppendWord(dp, "(@local)", getLocalParen, FW_COMPILE); pSys->pToLocalParen = dictAppendWord(dp, "(toLocal)", toLocalParen, FW_COMPILE); pSys->pGetLocal0 = dictAppendWord(dp, "(@local0)", getLocal0, FW_COMPILE); pSys->pToLocal0 = dictAppendWord(dp, "(toLocal0)",toLocal0, FW_COMPILE); pSys->pGetLocal1 = dictAppendWord(dp, "(@local1)", getLocal1, FW_COMPILE); pSys->pToLocal1 = dictAppendWord(dp, "(toLocal1)",toLocal1, FW_COMPILE); dictAppendWord(dp, "(local)", localParen, FW_COMPILE); pSys->pGet2LocalParen = dictAppendWord(dp, "(@2local)", get2LocalParen, FW_COMPILE); pSys->pTo2LocalParen = dictAppendWord(dp, "(to2Local)",to2LocalParen, FW_COMPILE); dictAppendWord(dp, "(2local)", twoLocalParen, FW_COMPILE); ficlSetEnv(pSys, "locals", FICL_TRUE); ficlSetEnv(pSys, "locals-ext", FICL_TRUE); ficlSetEnv(pSys, "#locals", FICL_MAX_LOCALS); #endif /* ** Optional MEMORY-ALLOC word set */ dictAppendWord(dp, "allocate", ansAllocate, FW_DEFAULT); dictAppendWord(dp, "free", ansFree, FW_DEFAULT); dictAppendWord(dp, "resize", ansResize, FW_DEFAULT); ficlSetEnv(pSys, "memory-alloc", FICL_TRUE); /* ** optional SEARCH-ORDER word set */ ficlCompileSearch(pSys); /* ** TOOLS and TOOLS EXT */ ficlCompileTools(pSys); /* ** FILE and FILE EXT */ #if FICL_WANT_FILE ficlCompileFile(pSys); #endif /* ** Ficl extras */ #if FICL_WANT_FLOAT dictAppendWord(dp, ".hash", dictHashSummary,FW_DEFAULT); #endif dictAppendWord(dp, ".ver", ficlVersion, FW_DEFAULT); dictAppendWord(dp, "-roll", minusRoll, FW_DEFAULT); dictAppendWord(dp, ">name", toName, FW_DEFAULT); dictAppendWord(dp, "add-parse-step", addParseStep, FW_DEFAULT); dictAppendWord(dp, "body>", fromBody, FW_DEFAULT); dictAppendWord(dp, "compare", compareString, FW_DEFAULT); /* STRING */ dictAppendWord(dp, "compare-insensitive", compareStringInsensitive, FW_DEFAULT); /* STRING */ dictAppendWord(dp, "compile-only", compileOnly, FW_DEFAULT); dictAppendWord(dp, "endif", endifCoIm, FW_COMPIMMED); dictAppendWord(dp, "last-word", getLastWord, FW_DEFAULT); dictAppendWord(dp, "hash", hash, FW_DEFAULT); dictAppendWord(dp, "objectify", setObjectFlag, FW_DEFAULT); dictAppendWord(dp, "?object", isObject, FW_DEFAULT); dictAppendWord(dp, "parse-word",parseNoCopy, FW_DEFAULT); dictAppendWord(dp, "sfind", sFind, FW_DEFAULT); dictAppendWord(dp, "sliteral", sLiteralCoIm, FW_COMPIMMED); /* STRING */ dictAppendWord(dp, "sprintf", ficlSprintf, FW_DEFAULT); dictAppendWord(dp, "strlen", ficlStrlen, FW_DEFAULT); dictAppendWord(dp, "q@", quadFetch, FW_DEFAULT); dictAppendWord(dp, "q!", quadStore, FW_DEFAULT); dictAppendWord(dp, "w@", wFetch, FW_DEFAULT); dictAppendWord(dp, "w!", wStore, FW_DEFAULT); dictAppendWord(dp, "x.", hexDot, FW_DEFAULT); #if FICL_WANT_USER dictAppendWord(dp, "(user)", userParen, FW_DEFAULT); dictAppendWord(dp, "user", userVariable, FW_DEFAULT); #endif #ifdef TESTMAIN dictAppendWord(dp, "random", ficlRandom, FW_DEFAULT); dictAppendWord(dp, "seed-random",ficlSeedRandom,FW_DEFAULT); #endif /* ** internal support words */ dictAppendWord(dp, "(create)", createParen, FW_COMPILE); pSys->pExitParen = dictAppendWord(dp, "(exit)", exitParen, FW_COMPILE); pSys->pSemiParen = dictAppendWord(dp, "(;)", semiParen, FW_COMPILE); pSys->pLitParen = dictAppendWord(dp, "(literal)", literalParen, FW_COMPILE); pSys->pTwoLitParen = dictAppendWord(dp, "(2literal)",twoLitParen, FW_COMPILE); pSys->pStringLit = dictAppendWord(dp, "(.\")", stringLit, FW_COMPILE); pSys->pCStringLit = dictAppendWord(dp, "(c\")", cstringLit, FW_COMPILE); pSys->pBranch0 = dictAppendWord(dp, "(branch0)", branch0, FW_COMPILE); pSys->pBranchParen = dictAppendWord(dp, "(branch)", branchParen, FW_COMPILE); pSys->pDoParen = dictAppendWord(dp, "(do)", doParen, FW_COMPILE); pSys->pDoesParen = dictAppendWord(dp, "(does>)", doesParen, FW_COMPILE); pSys->pQDoParen = dictAppendWord(dp, "(?do)", qDoParen, FW_COMPILE); pSys->pLoopParen = dictAppendWord(dp, "(loop)", loopParen, FW_COMPILE); pSys->pPLoopParen = dictAppendWord(dp, "(+loop)", plusLoopParen, FW_COMPILE); pSys->pInterpret = dictAppendWord(dp, "interpret", interpret, FW_DEFAULT); dictAppendWord(dp, "lookup", lookup, FW_DEFAULT); pSys->pOfParen = dictAppendWord(dp, "(of)", ofParen, FW_DEFAULT); dictAppendWord(dp, "(variable)",variableParen, FW_COMPILE); dictAppendWord(dp, "(constant)",constantParen, FW_COMPILE); dictAppendWord(dp, "(parse-step)", parseStepParen, FW_DEFAULT); pSys->pExitInner = dictAppendWord(dp, "exit-inner",ficlExitInner, FW_DEFAULT); /* ** Set up system's outer interpreter loop - maybe this should be in initSystem? */ pSys->pInterp[0] = pSys->pInterpret; pSys->pInterp[1] = pSys->pBranchParen; pSys->pInterp[2] = (FICL_WORD *)(void *)(-2); assert(dictCellsAvail(dp) > 0); return; } Index: projects/release-pkg/sys/boot/i386/Makefile =================================================================== --- projects/release-pkg/sys/boot/i386/Makefile (revision 295422) +++ projects/release-pkg/sys/boot/i386/Makefile (revision 295423) @@ -1,15 +1,19 @@ # $FreeBSD$ .include SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot \ libi386 libfirewire loader # special boot programs, 'self-extracting boot2+loader' SUBDIR+= pxeldr +.if ${MACHINE_CPUARCH} == "i386" +SUBDIR+= kgzldr +.endif + .if ${MK_ZFS} != "no" SUBDIR+= zfsboot gptzfsboot zfsloader .endif .include Index: projects/release-pkg/sys/boot =================================================================== --- projects/release-pkg/sys/boot (revision 295422) +++ projects/release-pkg/sys/boot (revision 295423) Property changes on: projects/release-pkg/sys/boot ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/boot:r295394-295422 Index: projects/release-pkg/sys/cam/scsi/scsi_xpt.c =================================================================== --- projects/release-pkg/sys/cam/scsi/scsi_xpt.c (revision 295422) +++ projects/release-pkg/sys/cam/scsi/scsi_xpt.c (revision 295423) @@ -1,3086 +1,3086 @@ /*- * Implementation of the SCSI Transport * * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. * 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, * without modification, immediately at the beginning of the file. * 2. 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 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for xpt_print below */ #include "opt_cam.h" struct scsi_quirk_entry { struct scsi_inquiry_pattern inq_pat; u_int8_t quirks; #define CAM_QUIRK_NOLUNS 0x01 #define CAM_QUIRK_NOVPDS 0x02 #define CAM_QUIRK_HILUNS 0x04 #define CAM_QUIRK_NOHILUNS 0x08 #define CAM_QUIRK_NORPTLUNS 0x10 u_int mintags; u_int maxtags; }; #define SCSI_QUIRK(dev) ((struct scsi_quirk_entry *)((dev)->quirk)) static int cam_srch_hi = 0; static int sysctl_cam_search_luns(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_kern_cam, OID_AUTO, cam_srch_hi, CTLTYPE_INT | CTLFLAG_RWTUN, 0, 0, sysctl_cam_search_luns, "I", "allow search above LUN 7 for SCSI3 and greater devices"); #define CAM_SCSI2_MAXLUN 8 #define CAM_CAN_GET_SIMPLE_LUN(x, i) \ ((((x)->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) == \ RPL_LUNDATA_ATYP_PERIPH) || \ (((x)->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) == \ RPL_LUNDATA_ATYP_FLAT)) #define CAM_GET_SIMPLE_LUN(lp, i, lval) \ if (((lp)->luns[(i)].lundata[0] & RPL_LUNDATA_ATYP_MASK) == \ RPL_LUNDATA_ATYP_PERIPH) { \ (lval) = (lp)->luns[(i)].lundata[1]; \ } else { \ (lval) = (lp)->luns[(i)].lundata[0]; \ (lval) &= RPL_LUNDATA_FLAT_LUN_MASK; \ (lval) <<= 8; \ (lval) |= (lp)->luns[(i)].lundata[1]; \ } #define CAM_GET_LUN(lp, i, lval) \ (lval) = scsi_8btou64((lp)->luns[(i)].lundata); \ (lval) = CAM_EXTLUN_BYTE_SWIZZLE(lval); /* * If we're not quirked to search <= the first 8 luns * and we are either quirked to search above lun 8, * or we're > SCSI-2 and we've enabled hilun searching, * or we're > SCSI-2 and the last lun was a success, * we can look for luns above lun 8. */ #define CAN_SRCH_HI_SPARSE(dv) \ (((SCSI_QUIRK(dv)->quirks & CAM_QUIRK_NOHILUNS) == 0) \ && ((SCSI_QUIRK(dv)->quirks & CAM_QUIRK_HILUNS) \ || (SID_ANSI_REV(&dv->inq_data) > SCSI_REV_2 && cam_srch_hi))) #define CAN_SRCH_HI_DENSE(dv) \ (((SCSI_QUIRK(dv)->quirks & CAM_QUIRK_NOHILUNS) == 0) \ && ((SCSI_QUIRK(dv)->quirks & CAM_QUIRK_HILUNS) \ || (SID_ANSI_REV(&dv->inq_data) > SCSI_REV_2))) static periph_init_t probe_periph_init; static struct periph_driver probe_driver = { probe_periph_init, "probe", TAILQ_HEAD_INITIALIZER(probe_driver.units), /* generation */ 0, CAM_PERIPH_DRV_EARLY }; PERIPHDRIVER_DECLARE(probe, probe_driver); typedef enum { PROBE_TUR, PROBE_INQUIRY, /* this counts as DV0 for Basic Domain Validation */ PROBE_FULL_INQUIRY, PROBE_REPORT_LUNS, PROBE_MODE_SENSE, PROBE_SUPPORTED_VPD_LIST, PROBE_DEVICE_ID, PROBE_EXTENDED_INQUIRY, PROBE_SERIAL_NUM, PROBE_TUR_FOR_NEGOTIATION, PROBE_INQUIRY_BASIC_DV1, PROBE_INQUIRY_BASIC_DV2, PROBE_DV_EXIT, PROBE_DONE, PROBE_INVALID } probe_action; static char *probe_action_text[] = { "PROBE_TUR", "PROBE_INQUIRY", "PROBE_FULL_INQUIRY", "PROBE_REPORT_LUNS", "PROBE_MODE_SENSE", "PROBE_SUPPORTED_VPD_LIST", "PROBE_DEVICE_ID", "PROBE_EXTENDED_INQUIRY", "PROBE_SERIAL_NUM", "PROBE_TUR_FOR_NEGOTIATION", "PROBE_INQUIRY_BASIC_DV1", "PROBE_INQUIRY_BASIC_DV2", "PROBE_DV_EXIT", "PROBE_DONE", "PROBE_INVALID" }; #define PROBE_SET_ACTION(softc, newaction) \ do { \ char **text; \ text = probe_action_text; \ CAM_DEBUG((softc)->periph->path, CAM_DEBUG_PROBE, \ ("Probe %s to %s\n", text[(softc)->action], \ text[(newaction)])); \ (softc)->action = (newaction); \ } while(0) typedef enum { PROBE_INQUIRY_CKSUM = 0x01, PROBE_SERIAL_CKSUM = 0x02, PROBE_NO_ANNOUNCE = 0x04, PROBE_EXTLUN = 0x08 } probe_flags; typedef struct { TAILQ_HEAD(, ccb_hdr) request_ccbs; probe_action action; union ccb saved_ccb; probe_flags flags; MD5_CTX context; u_int8_t digest[16]; struct cam_periph *periph; } probe_softc; static const char quantum[] = "QUANTUM"; static const char sony[] = "SONY"; static const char west_digital[] = "WDIGTL"; static const char samsung[] = "SAMSUNG"; static const char seagate[] = "SEAGATE"; static const char microp[] = "MICROP"; static struct scsi_quirk_entry scsi_quirk_table[] = { { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP39100*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP34550*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP32275*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, microp, "4421-07*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, "HP", "C372*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, microp, "3391*", "x43h" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Unfortunately, the Quantum Atlas III has the same * problem as the Atlas II drives above. * Reported by: "Johan Granlund" * * For future reference, the drive with the problem was: * QUANTUM QM39100TD-SW N1B0 * * It's possible that Quantum will fix the problem in later * firmware revisions. If that happens, the quirk entry * will need to be made specific to the firmware revisions * with the problem. * */ /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "QM39100*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* * 18 Gig Atlas III, same problem as the 9G version. * Reported by: Andre Albsmeier * * * For future reference, the drive with the problem was: * QUANTUM QM318000TD-S N491 */ /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "QM318000*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* * Broken tagged queuing drive * Reported by: Bret Ford * and: Martin Renters */ { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST410800*", "71*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, /* * The Seagate Medalist Pro drives have very poor write * performance with anything more than 2 tags. * * Reported by: Paul van der Zwan * Drive: * * Reported by: Jeremy Lea * Drive: * * No one has actually reported that the 9G version * (ST39140*) of the Medalist Pro has the same problem, but * we're assuming that it does because the 4G and 6.5G * versions of the drive are broken. */ { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST34520*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST36530*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST39140*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { /* * Experiences command timeouts under load with a * tag count higher than 55. */ { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST3146855LW", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/55 }, { /* * Slow when tagged queueing is enabled. Write performance * steadily drops off with more and more concurrent * transactions. Best sequential write performance with * tagged queueing turned off and write caching turned on. * * PR: kern/10398 * Submitted by: Hideaki Okada * Drive: DCAS-34330 w/ "S65A" firmware. * * The drive with the problem had the "S65A" firmware * revision, and has also been reported (by Stephen J. * Roznowski ) for a drive with the "S61A" * firmware revision. * * Although no one has reported problems with the 2 gig * version of the DCAS drive, the assumption is that it * has the same problems as the 4 gig version. Therefore * this quirk entries disables tagged queueing for all * DCAS drives. */ { T_DIRECT, SIP_MEDIA_FIXED, "IBM", "DCAS*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_REMOVABLE, "iomega", "jaz*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, "CONNER", "CFP2107*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* This does not support other than LUN 0 */ { T_DIRECT, SIP_MEDIA_FIXED, "VMware*", "*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/2, /*maxtags*/255 }, { /* * Broken tagged queuing drive. * Submitted by: * NAKAJI Hiroyuki * in PR kern/9535 */ { T_DIRECT, SIP_MEDIA_FIXED, samsung, "WN34324U*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Slow when tagged queueing is enabled. (1.5MB/sec versus * 8MB/sec.) * Submitted by: Andrew Gallatin * Best performance with these drives is achieved with * tagged queueing turned off, and write caching turned on. */ { T_DIRECT, SIP_MEDIA_FIXED, west_digital, "WDE*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Slow when tagged queueing is enabled. (1.5MB/sec versus * 8MB/sec.) * Submitted by: Andrew Gallatin * Best performance with these drives is achieved with * tagged queueing turned off, and write caching turned on. */ { T_DIRECT, SIP_MEDIA_FIXED, west_digital, "ENTERPRISE", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Doesn't handle queue full condition correctly, * so we need to limit maxtags to what the device * can handle instead of determining this automatically. */ { T_DIRECT, SIP_MEDIA_FIXED, samsung, "WN321010S*", "*" }, /*quirks*/0, /*mintags*/2, /*maxtags*/32 }, { /* Really only one LUN */ { T_ENCLOSURE, SIP_MEDIA_FIXED, "SUN", "SENA", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* I can't believe we need a quirk for DPT volumes. */ { T_ANY, SIP_MEDIA_FIXED|SIP_MEDIA_REMOVABLE, "DPT", "*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/255 }, { /* * Many Sony CDROM drives don't like multi-LUN probing. */ { T_CDROM, SIP_MEDIA_REMOVABLE, sony, "CD-ROM CDU*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * This drive doesn't like multiple LUN probing. * Submitted by: Parag Patel */ { T_WORM, SIP_MEDIA_REMOVABLE, sony, "CD-R CDU9*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { { T_WORM, SIP_MEDIA_REMOVABLE, "YAMAHA", "CDR100*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * The 8200 doesn't like multi-lun probing, and probably * don't like serial number requests either. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "EXABYTE", "EXB-8200*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * Let's try the same as above, but for a drive that says * it's an IPL-6860 but is actually an EXB 8200. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "EXABYTE", "IPL-6860*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * These Hitachi drives don't like multi-lun probing. * The PR submitter has a DK319H, but says that the Linux * kernel has a similar work-around for the DK312 and DK314, * so all DK31* drives are quirked here. * PR: misc/18793 * Submitted by: Paul Haddad */ { T_DIRECT, SIP_MEDIA_FIXED, "HITACHI", "DK31*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/2, /*maxtags*/255 }, { /* * The Hitachi CJ series with J8A8 firmware apparantly has * problems with tagged commands. * PR: 23536 * Reported by: amagai@nue.org */ { T_DIRECT, SIP_MEDIA_FIXED, "HITACHI", "DK32CJ*", "J8A8" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * These are the large storage arrays. * Submitted by: William Carrel */ { T_DIRECT, SIP_MEDIA_FIXED, "HITACHI", "OPEN*", "*" }, CAM_QUIRK_HILUNS, 2, 1024 }, { /* * This old revision of the TDC3600 is also SCSI-1, and * hangs upon serial number probing. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG", " TDC 3600", "U07:" }, CAM_QUIRK_NOVPDS, /*mintags*/0, /*maxtags*/0 }, { /* * Would repond to all LUNs if asked for. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "CALIPER", "CP150", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * Would repond to all LUNs if asked for. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY", "96X2*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* Submitted by: Matthew Dodd */ { T_PROCESSOR, SIP_MEDIA_FIXED, "Cabletrn", "EA41*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* Submitted by: Matthew Dodd */ { T_PROCESSOR, SIP_MEDIA_FIXED, "CABLETRN", "EA41*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* TeraSolutions special settings for TRC-22 RAID */ { T_DIRECT, SIP_MEDIA_FIXED, "TERASOLU", "TRC-22", "*" }, /*quirks*/0, /*mintags*/55, /*maxtags*/255 }, { /* Veritas Storage Appliance */ { T_DIRECT, SIP_MEDIA_FIXED, "VERITAS", "*", "*" }, CAM_QUIRK_HILUNS, /*mintags*/2, /*maxtags*/1024 }, { /* * Would respond to all LUNs. Device type and removable * flag are jumper-selectable. */ { T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, "MaxOptix", "Tahiti 1", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* EasyRAID E5A aka. areca ARC-6010 */ { T_DIRECT, SIP_MEDIA_FIXED, "easyRAID", "*", "*" }, CAM_QUIRK_NOHILUNS, /*mintags*/2, /*maxtags*/255 }, { { T_ENCLOSURE, SIP_MEDIA_FIXED, "DP", "BACKPLANE", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { { T_DIRECT, SIP_MEDIA_REMOVABLE, "Garmin", "*", "*" }, CAM_QUIRK_NORPTLUNS, /*mintags*/2, /*maxtags*/255 }, { /* Default tagged queuing parameters for all devices */ { T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, /*vendor*/"*", /*product*/"*", /*revision*/"*" }, /*quirks*/0, /*mintags*/2, /*maxtags*/255 }, }; static const int scsi_quirk_table_size = sizeof(scsi_quirk_table) / sizeof(*scsi_quirk_table); static cam_status proberegister(struct cam_periph *periph, void *arg); static void probeschedule(struct cam_periph *probe_periph); static void probestart(struct cam_periph *periph, union ccb *start_ccb); static void proberequestdefaultnegotiation(struct cam_periph *periph); static int proberequestbackoff(struct cam_periph *periph, struct cam_ed *device); static void probedone(struct cam_periph *periph, union ccb *done_ccb); static void probe_purge_old(struct cam_path *path, struct scsi_report_luns_data *new, probe_flags flags); static void probecleanup(struct cam_periph *periph); static void scsi_find_quirk(struct cam_ed *device); static void scsi_scan_bus(struct cam_periph *periph, union ccb *ccb); static void scsi_scan_lun(struct cam_periph *periph, struct cam_path *path, cam_flags flags, union ccb *ccb); static void xptscandone(struct cam_periph *periph, union ccb *done_ccb); static struct cam_ed * scsi_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); static void scsi_devise_transport(struct cam_path *path); static void scsi_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_path *path, int async_update); static void scsi_toggle_tags(struct cam_path *path); static void scsi_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg); static void scsi_action(union ccb *start_ccb); static void scsi_announce_periph(struct cam_periph *periph); static struct xpt_xport scsi_xport = { .alloc_device = scsi_alloc_device, .action = scsi_action, .async = scsi_dev_async, .announce = scsi_announce_periph, }; struct xpt_xport * scsi_get_xport(void) { return (&scsi_xport); } static void probe_periph_init() { } static cam_status proberegister(struct cam_periph *periph, void *arg) { union ccb *request_ccb; /* CCB representing the probe request */ cam_status status; probe_softc *softc; request_ccb = (union ccb *)arg; if (request_ccb == NULL) { printf("proberegister: no probe CCB, " "can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (probe_softc *)malloc(sizeof(*softc), M_CAMXPT, M_NOWAIT); if (softc == NULL) { printf("proberegister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } TAILQ_INIT(&softc->request_ccbs); TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, periph_links.tqe); softc->flags = 0; periph->softc = softc; softc->periph = periph; softc->action = PROBE_INVALID; status = cam_periph_acquire(periph); if (status != CAM_REQ_CMP) { return (status); } CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Probe started\n")); scsi_devise_transport(periph->path); /* * Ensure we've waited at least a bus settle * delay before attempting to probe the device. * For HBAs that don't do bus resets, this won't make a difference. */ cam_periph_freeze_after_event(periph, &periph->path->bus->last_reset, scsi_delay); probeschedule(periph); return(CAM_REQ_CMP); } static void probeschedule(struct cam_periph *periph) { struct ccb_pathinq cpi; union ccb *ccb; probe_softc *softc; softc = (probe_softc *)periph->softc; ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); /* * If a device has gone away and another device, or the same one, * is back in the same place, it should have a unit attention * condition pending. It will not report the unit attention in * response to an inquiry, which may leave invalid transfer * negotiations in effect. The TUR will reveal the unit attention * condition. Only send the TUR for lun 0, since some devices * will get confused by commands other than inquiry to non-existent * luns. If you think a device has gone away start your scan from * lun 0. This will insure that any bogus transfer settings are * invalidated. * * If we haven't seen the device before and the controller supports * some kind of transfer negotiation, negotiate with the first * sent command if no bus reset was performed at startup. This * ensures that the device is not confused by transfer negotiation * settings left over by loader or BIOS action. */ if (((ccb->ccb_h.path->device->flags & CAM_DEV_UNCONFIGURED) == 0) && (ccb->ccb_h.target_lun == 0)) { PROBE_SET_ACTION(softc, PROBE_TUR); } else if ((cpi.hba_inquiry & (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) != 0 && (cpi.hba_misc & PIM_NOBUSRESET) != 0) { proberequestdefaultnegotiation(periph); PROBE_SET_ACTION(softc, PROBE_INQUIRY); } else { PROBE_SET_ACTION(softc, PROBE_INQUIRY); } if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE) softc->flags |= PROBE_NO_ANNOUNCE; else softc->flags &= ~PROBE_NO_ANNOUNCE; if (cpi.hba_misc & PIM_EXTLUNS) softc->flags |= PROBE_EXTLUN; else softc->flags &= ~PROBE_EXTLUN; xpt_schedule(periph, CAM_PRIORITY_XPT); } static void probestart(struct cam_periph *periph, union ccb *start_ccb) { /* Probe the device that our peripheral driver points to */ struct ccb_scsiio *csio; probe_softc *softc; CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probestart\n")); softc = (probe_softc *)periph->softc; csio = &start_ccb->csio; again: switch (softc->action) { case PROBE_TUR: case PROBE_TUR_FOR_NEGOTIATION: case PROBE_DV_EXIT: { scsi_test_unit_ready(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, /*timeout*/60000); break; } case PROBE_INQUIRY: case PROBE_FULL_INQUIRY: case PROBE_INQUIRY_BASIC_DV1: case PROBE_INQUIRY_BASIC_DV2: { u_int inquiry_len; struct scsi_inquiry_data *inq_buf; inq_buf = &periph->path->device->inq_data; /* * If the device is currently configured, we calculate an * MD5 checksum of the inquiry data, and if the serial number * length is greater than 0, add the serial number data * into the checksum as well. Once the inquiry and the * serial number check finish, we attempt to figure out * whether we still have the same device. */ if (((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) && ((softc->flags & PROBE_INQUIRY_CKSUM) == 0)) { MD5Init(&softc->context); MD5Update(&softc->context, (unsigned char *)inq_buf, sizeof(struct scsi_inquiry_data)); softc->flags |= PROBE_INQUIRY_CKSUM; if (periph->path->device->serial_num_len > 0) { MD5Update(&softc->context, periph->path->device->serial_num, periph->path->device->serial_num_len); softc->flags |= PROBE_SERIAL_CKSUM; } MD5Final(softc->digest, &softc->context); } if (softc->action == PROBE_INQUIRY) inquiry_len = SHORT_INQUIRY_LENGTH; else inquiry_len = SID_ADDITIONAL_LENGTH(inq_buf); /* * Some parallel SCSI devices fail to send an * ignore wide residue message when dealing with * odd length inquiry requests. Round up to be * safe. */ inquiry_len = roundup2(inquiry_len, 2); if (softc->action == PROBE_INQUIRY_BASIC_DV1 || softc->action == PROBE_INQUIRY_BASIC_DV2) { inq_buf = malloc(inquiry_len, M_CAMXPT, M_NOWAIT); } if (inq_buf == NULL) { xpt_print(periph->path, "malloc failure- skipping Basic" "Domain Validation\n"); PROBE_SET_ACTION(softc, PROBE_DV_EXIT); scsi_test_unit_ready(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, /*timeout*/60000); break; } scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)inq_buf, inquiry_len, /*evpd*/FALSE, /*page_code*/0, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } case PROBE_REPORT_LUNS: { void *rp; rp = malloc(periph->path->target->rpl_size, M_CAMXPT, M_NOWAIT | M_ZERO); if (rp == NULL) { struct scsi_inquiry_data *inq_buf; inq_buf = &periph->path->device->inq_data; xpt_print(periph->path, "Unable to alloc report luns storage\n"); if (INQ_DATA_TQ_ENABLED(inq_buf)) PROBE_SET_ACTION(softc, PROBE_MODE_SENSE); else PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST); goto again; } scsi_report_luns(csio, 5, probedone, MSG_SIMPLE_Q_TAG, RPL_REPORT_DEFAULT, rp, periph->path->target->rpl_size, SSD_FULL_SIZE, 60000); break; break; } case PROBE_MODE_SENSE: { void *mode_buf; int mode_buf_len; mode_buf_len = sizeof(struct scsi_mode_header_6) + sizeof(struct scsi_mode_blk_desc) + sizeof(struct scsi_control_page); mode_buf = malloc(mode_buf_len, M_CAMXPT, M_NOWAIT); if (mode_buf != NULL) { scsi_mode_sense(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SMS_CONTROL_MODE_PAGE, mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60000); break; } xpt_print(periph->path, "Unable to mode sense control page - " "malloc failure\n"); PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST); } /* FALLTHROUGH */ case PROBE_SUPPORTED_VPD_LIST: { struct scsi_vpd_supported_page_list *vpd_list; struct cam_ed *device; vpd_list = NULL; device = periph->path->device; if ((SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOVPDS) == 0) vpd_list = malloc(sizeof(*vpd_list), M_CAMXPT, M_NOWAIT | M_ZERO); if (vpd_list != NULL) { scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)vpd_list, sizeof(*vpd_list), /*evpd*/TRUE, SVPD_SUPPORTED_PAGE_LIST, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } done: /* * We'll have to do without, let our probedone * routine finish up for us. */ start_ccb->csio.data_ptr = NULL; cam_freeze_devq(periph->path); cam_periph_doacquire(periph); probedone(periph, start_ccb); return; } case PROBE_DEVICE_ID: { struct scsi_vpd_device_id *devid; devid = NULL; if (scsi_vpd_supported_page(periph, SVPD_DEVICE_ID)) devid = malloc(SVPD_DEVICE_ID_MAX_SIZE, M_CAMXPT, M_NOWAIT | M_ZERO); if (devid != NULL) { scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (uint8_t *)devid, SVPD_DEVICE_ID_MAX_SIZE, /*evpd*/TRUE, SVPD_DEVICE_ID, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } goto done; } case PROBE_EXTENDED_INQUIRY: { struct scsi_vpd_extended_inquiry_data *ext_inq; ext_inq = NULL; if (scsi_vpd_supported_page(periph, SVPD_EXTENDED_INQUIRY_DATA)) ext_inq = malloc(sizeof(*ext_inq), M_CAMXPT, M_NOWAIT | M_ZERO); if (ext_inq != NULL) { scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (uint8_t *)ext_inq, sizeof(*ext_inq), /*evpd*/TRUE, SVPD_EXTENDED_INQUIRY_DATA, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } /* * We'll have to do without, let our probedone * routine finish up for us. */ goto done; } case PROBE_SERIAL_NUM: { struct scsi_vpd_unit_serial_number *serial_buf; struct cam_ed* device; serial_buf = NULL; device = periph->path->device; if (device->serial_num != NULL) { free(device->serial_num, M_CAMXPT); device->serial_num = NULL; device->serial_num_len = 0; } if (scsi_vpd_supported_page(periph, SVPD_UNIT_SERIAL_NUMBER)) serial_buf = (struct scsi_vpd_unit_serial_number *) malloc(sizeof(*serial_buf), M_CAMXPT, M_NOWAIT|M_ZERO); if (serial_buf != NULL) { scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)serial_buf, sizeof(*serial_buf), /*evpd*/TRUE, SVPD_UNIT_SERIAL_NUMBER, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } goto done; } default: panic("probestart: invalid action state 0x%x\n", softc->action); } start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; cam_periph_doacquire(periph); xpt_action(start_ccb); } static void proberequestdefaultnegotiation(struct cam_periph *periph) { struct ccb_trans_settings cts; xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_USER_SETTINGS; xpt_action((union ccb *)&cts); if (cam_ccb_status((union ccb *)&cts) != CAM_REQ_CMP) { return; } cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); } /* * Backoff Negotiation Code- only pertinent for SPI devices. */ static int proberequestbackoff(struct cam_periph *periph, struct cam_ed *device) { struct ccb_trans_settings cts; struct ccb_trans_settings_spi *spi; memset(&cts, 0, sizeof (cts)); xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); if (cam_ccb_status((union ccb *)&cts) != CAM_REQ_CMP) { if (bootverbose) { xpt_print(periph->path, "failed to get current device settings\n"); } return (0); } if (cts.transport != XPORT_SPI) { if (bootverbose) { xpt_print(periph->path, "not SPI transport\n"); } return (0); } spi = &cts.xport_specific.spi; /* * We cannot renegotiate sync rate if we don't have one. */ if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) { if (bootverbose) { xpt_print(periph->path, "no sync rate known\n"); } return (0); } /* * We'll assert that we don't have to touch PPR options- the * SIM will see what we do with period and offset and adjust * the PPR options as appropriate. */ /* * A sync rate with unknown or zero offset is nonsensical. * A sync period of zero means Async. */ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0 || spi->sync_offset == 0 || spi->sync_period == 0) { if (bootverbose) { xpt_print(periph->path, "no sync rate available\n"); } return (0); } if (device->flags & CAM_DEV_DV_HIT_BOTTOM) { CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("hit async: giving up on DV\n")); return (0); } /* * Jump sync_period up by one, but stop at 5MHz and fall back to Async. * We don't try to remember 'last' settings to see if the SIM actually * gets into the speed we want to set. We check on the SIM telling * us that a requested speed is bad, but otherwise don't try and * check the speed due to the asynchronous and handshake nature * of speed setting. */ spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET; for (;;) { spi->sync_period++; if (spi->sync_period >= 0xf) { spi->sync_period = 0; spi->sync_offset = 0; CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("setting to async for DV\n")); /* * Once we hit async, we don't want to try * any more settings. */ device->flags |= CAM_DEV_DV_HIT_BOTTOM; } else if (bootverbose) { CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("DV: period 0x%x\n", spi->sync_period)); printf("setting period to 0x%x\n", spi->sync_period); } cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); if (cam_ccb_status((union ccb *)&cts) != CAM_REQ_CMP) { break; } CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("DV: failed to set period 0x%x\n", spi->sync_period)); if (spi->sync_period == 0) { return (0); } } return (1); } #define CCB_COMPLETED_OK(ccb) (((ccb).status & CAM_STATUS_MASK) == CAM_REQ_CMP) static void probedone(struct cam_periph *periph, union ccb *done_ccb) { probe_softc *softc; struct cam_path *path; struct scsi_inquiry_data *inq_buf; u_int32_t priority; CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n")); softc = (probe_softc *)periph->softc; path = done_ccb->ccb_h.path; priority = done_ccb->ccb_h.pinfo.priority; switch (softc->action) { case PROBE_TUR: { if (cam_ccb_status(done_ccb) != CAM_REQ_CMP) { if (cam_periph_error(done_ccb, 0, SF_NO_PRINT, NULL) == ERESTART) { outr: /* Drop freeze taken due to CAM_DEV_QFREEZE */ cam_release_devq(path, 0, 0, 0, FALSE); return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } PROBE_SET_ACTION(softc, PROBE_INQUIRY); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); out: /* Drop freeze taken due to CAM_DEV_QFREEZE and release. */ cam_release_devq(path, 0, 0, 0, FALSE); cam_periph_release_locked(periph); return; } case PROBE_INQUIRY: case PROBE_FULL_INQUIRY: { if (cam_ccb_status(done_ccb) == CAM_REQ_CMP) { u_int8_t periph_qual; path->device->flags |= CAM_DEV_INQUIRY_DATA_VALID; scsi_find_quirk(path->device); inq_buf = &path->device->inq_data; periph_qual = SID_QUAL(inq_buf); if (periph_qual == SID_QUAL_LU_CONNECTED || periph_qual == SID_QUAL_LU_OFFLINE) { u_int8_t len; /* * We conservatively request only * SHORT_INQUIRY_LEN bytes of inquiry * information during our first try * at sending an INQUIRY. If the device * has more information to give, * perform a second request specifying * the amount of information the device * is willing to give. */ len = inq_buf->additional_length + offsetof(struct scsi_inquiry_data, additional_length) + 1; if (softc->action == PROBE_INQUIRY && len > SHORT_INQUIRY_LENGTH) { PROBE_SET_ACTION(softc, PROBE_FULL_INQUIRY); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); goto out; } scsi_devise_transport(path); if (path->device->lun_id == 0 && SID_ANSI_REV(inq_buf) > SCSI_REV_SPC2 && (SCSI_QUIRK(path->device)->quirks & CAM_QUIRK_NORPTLUNS) == 0) { PROBE_SET_ACTION(softc, PROBE_REPORT_LUNS); /* * Start with room for *one* lun. */ periph->path->target->rpl_size = 16; } else if (INQ_DATA_TQ_ENABLED(inq_buf)) PROBE_SET_ACTION(softc, PROBE_MODE_SENSE); else PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST); if (path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); } xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); goto out; } else if (path->device->lun_id == 0 && SID_ANSI_REV(inq_buf) >= SCSI_REV_SPC2 && (SCSI_QUIRK(path->device)->quirks & CAM_QUIRK_NORPTLUNS) == 0) { PROBE_SET_ACTION(softc, PROBE_REPORT_LUNS); periph->path->target->rpl_size = 16; xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); goto out; } } else if (cam_periph_error(done_ccb, 0, done_ccb->ccb_h.target_lun > 0 ? SF_RETRY_UA|SF_QUIET_IR : SF_RETRY_UA, &softc->saved_ccb) == ERESTART) { goto outr; } else { if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } path->device->flags &= ~CAM_DEV_INQUIRY_DATA_VALID; } /* * If we get to this point, we got an error status back * from the inquiry and the error status doesn't require * automatically retrying the command. Therefore, the * inquiry failed. If we had inquiry information before * for this device, but this latest inquiry command failed, * the device has probably gone away. If this device isn't * already marked unconfigured, notify the peripheral * drivers that this device is no more. */ if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0) /* Send the async notification. */ xpt_async(AC_LOST_DEVICE, path, NULL); PROBE_SET_ACTION(softc, PROBE_INVALID); xpt_release_ccb(done_ccb); break; } case PROBE_REPORT_LUNS: { struct ccb_scsiio *csio; struct scsi_report_luns_data *lp; u_int nlun, maxlun; csio = &done_ccb->csio; lp = (struct scsi_report_luns_data *)csio->data_ptr; nlun = scsi_4btoul(lp->length) / 8; maxlun = (csio->dxfer_len / 8) - 1; if (cam_ccb_status(done_ccb) != CAM_REQ_CMP) { if (cam_periph_error(done_ccb, 0, done_ccb->ccb_h.target_lun > 0 ? SF_RETRY_UA|SF_QUIET_IR : SF_RETRY_UA, &softc->saved_ccb) == ERESTART) { goto outr; } if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { xpt_release_devq(done_ccb->ccb_h.path, 1, TRUE); } free(lp, M_CAMXPT); lp = NULL; } else if (nlun > maxlun) { /* * Reallocate and retry to cover all luns */ CAM_DEBUG(path, CAM_DEBUG_PROBE, ("Probe: reallocating REPORT_LUNS for %u luns\n", nlun)); free(lp, M_CAMXPT); path->target->rpl_size = (nlun << 3) + 8; xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); goto out; } else if (nlun == 0) { /* * If there don't appear to be any luns, bail. */ free(lp, M_CAMXPT); lp = NULL; } else { lun_id_t lun; int idx; CAM_DEBUG(path, CAM_DEBUG_PROBE, ("Probe: %u lun(s) reported\n", nlun)); CAM_GET_LUN(lp, 0, lun); /* * If the first lun is not lun 0, then either there * is no lun 0 in the list, or the list is unsorted. */ if (lun != 0) { for (idx = 0; idx < nlun; idx++) { CAM_GET_LUN(lp, idx, lun); if (lun == 0) { break; } } if (idx != nlun) { uint8_t tlun[8]; memcpy(tlun, lp->luns[0].lundata, 8); memcpy(lp->luns[0].lundata, lp->luns[idx].lundata, 8); memcpy(lp->luns[idx].lundata, tlun, 8); CAM_DEBUG(path, CAM_DEBUG_PROBE, ("lun 0 in position %u\n", idx)); } } /* * If we have an old lun list, We can either * retest luns that appear to have been dropped, * or just nuke them. We'll opt for the latter. * This function will also install the new list * in the target structure. */ probe_purge_old(path, lp, softc->flags); lp = NULL; } inq_buf = &path->device->inq_data; if (path->device->flags & CAM_DEV_INQUIRY_DATA_VALID && (SID_QUAL(inq_buf) == SID_QUAL_LU_CONNECTED || SID_QUAL(inq_buf) == SID_QUAL_LU_OFFLINE)) { if (INQ_DATA_TQ_ENABLED(inq_buf)) PROBE_SET_ACTION(softc, PROBE_MODE_SENSE); else PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); goto out; } if (lp) { free(lp, M_CAMXPT); } PROBE_SET_ACTION(softc, PROBE_INVALID); xpt_release_ccb(done_ccb); break; } case PROBE_MODE_SENSE: { struct ccb_scsiio *csio; struct scsi_mode_header_6 *mode_hdr; csio = &done_ccb->csio; mode_hdr = (struct scsi_mode_header_6 *)csio->data_ptr; if (cam_ccb_status(done_ccb) == CAM_REQ_CMP) { struct scsi_control_page *page; u_int8_t *offset; offset = ((u_int8_t *)&mode_hdr[1]) + mode_hdr->blk_desc_len; page = (struct scsi_control_page *)offset; path->device->queue_flags = page->queue_flags; } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { goto outr; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } xpt_release_ccb(done_ccb); free(mode_hdr, M_CAMXPT); PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST); xpt_schedule(periph, priority); goto out; } case PROBE_SUPPORTED_VPD_LIST: { struct ccb_scsiio *csio; struct scsi_vpd_supported_page_list *page_list; csio = &done_ccb->csio; page_list = (struct scsi_vpd_supported_page_list *)csio->data_ptr; if (path->device->supported_vpds != NULL) { free(path->device->supported_vpds, M_CAMXPT); path->device->supported_vpds = NULL; path->device->supported_vpds_len = 0; } if (page_list == NULL) { /* * Don't process the command as it was never sent */ } else if (CCB_COMPLETED_OK(csio->ccb_h)) { /* Got vpd list */ path->device->supported_vpds_len = page_list->length + SVPD_SUPPORTED_PAGES_HDR_LEN; path->device->supported_vpds = (uint8_t *)page_list; xpt_release_ccb(done_ccb); PROBE_SET_ACTION(softc, PROBE_DEVICE_ID); xpt_schedule(periph, priority); goto out; } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { goto outr; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } if (page_list) free(page_list, M_CAMXPT); /* No VPDs available, skip to device check. */ csio->data_ptr = NULL; goto probe_device_check; } case PROBE_DEVICE_ID: { struct scsi_vpd_device_id *devid; struct ccb_scsiio *csio; uint32_t length = 0; csio = &done_ccb->csio; devid = (struct scsi_vpd_device_id *)csio->data_ptr; /* Clean up from previous instance of this device */ if (path->device->device_id != NULL) { path->device->device_id_len = 0; free(path->device->device_id, M_CAMXPT); path->device->device_id = NULL; } if (devid == NULL) { /* Don't process the command as it was never sent */ } else if (CCB_COMPLETED_OK(csio->ccb_h)) { length = scsi_2btoul(devid->length); if (length != 0) { /* * NB: device_id_len is actual response * size, not buffer size. */ path->device->device_id_len = length + SVPD_DEVICE_ID_HDR_LEN; path->device->device_id = (uint8_t *)devid; } } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA, &softc->saved_ccb) == ERESTART) { goto outr; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* Free the device id space if we don't use it */ if (devid && length == 0) free(devid, M_CAMXPT); xpt_release_ccb(done_ccb); PROBE_SET_ACTION(softc, PROBE_EXTENDED_INQUIRY); xpt_schedule(periph, priority); goto out; } case PROBE_EXTENDED_INQUIRY: { struct scsi_vpd_extended_inquiry_data *ext_inq; struct ccb_scsiio *csio; int32_t length = 0; csio = &done_ccb->csio; ext_inq = (struct scsi_vpd_extended_inquiry_data *) csio->data_ptr; if (path->device->ext_inq != NULL) { path->device->ext_inq_len = 0; free(path->device->ext_inq, M_CAMXPT); path->device->ext_inq = NULL; } if (ext_inq == NULL) { /* Don't process the command as it was never sent */ } else if (CCB_COMPLETED_OK(csio->ccb_h)) { length = scsi_2btoul(ext_inq->page_length) + __offsetof(struct scsi_vpd_extended_inquiry_data, flags1); length = min(length, sizeof(*ext_inq)); length -= csio->resid; if (length > 0) { path->device->ext_inq_len = length; path->device->ext_inq = (uint8_t *)ext_inq; } } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA, &softc->saved_ccb) == ERESTART) { - return; + goto outr; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* Free the device id space if we don't use it */ if (ext_inq && length <= 0) free(ext_inq, M_CAMXPT); xpt_release_ccb(done_ccb); PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM); xpt_schedule(periph, priority); goto out; } probe_device_check: case PROBE_SERIAL_NUM: { struct ccb_scsiio *csio; struct scsi_vpd_unit_serial_number *serial_buf; u_int32_t priority; int changed; int have_serialnum; changed = 1; have_serialnum = 0; csio = &done_ccb->csio; priority = done_ccb->ccb_h.pinfo.priority; serial_buf = (struct scsi_vpd_unit_serial_number *)csio->data_ptr; if (serial_buf == NULL) { /* * Don't process the command as it was never sent */ } else if (cam_ccb_status(done_ccb) == CAM_REQ_CMP && (serial_buf->length > 0)) { have_serialnum = 1; path->device->serial_num = (u_int8_t *)malloc((serial_buf->length + 1), M_CAMXPT, M_NOWAIT); if (path->device->serial_num != NULL) { memcpy(path->device->serial_num, serial_buf->serial_num, serial_buf->length); path->device->serial_num_len = serial_buf->length; path->device->serial_num[serial_buf->length] = '\0'; } } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { goto outr; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* * Let's see if we have seen this device before. */ if ((softc->flags & PROBE_INQUIRY_CKSUM) != 0) { MD5_CTX context; u_int8_t digest[16]; MD5Init(&context); MD5Update(&context, (unsigned char *)&path->device->inq_data, sizeof(struct scsi_inquiry_data)); if (have_serialnum) MD5Update(&context, serial_buf->serial_num, serial_buf->length); MD5Final(digest, &context); if (bcmp(softc->digest, digest, 16) == 0) changed = 0; /* * XXX Do we need to do a TUR in order to ensure * that the device really hasn't changed??? */ if ((changed != 0) && ((softc->flags & PROBE_NO_ANNOUNCE) == 0)) xpt_async(AC_LOST_DEVICE, path, NULL); } if (serial_buf != NULL) free(serial_buf, M_CAMXPT); if (changed != 0) { /* * Now that we have all the necessary * information to safely perform transfer * negotiations... Controllers don't perform * any negotiation or tagged queuing until * after the first XPT_SET_TRAN_SETTINGS ccb is * received. So, on a new device, just retrieve * the user settings, and set them as the current * settings to set the device up. */ proberequestdefaultnegotiation(periph); xpt_release_ccb(done_ccb); /* * Perform a TUR to allow the controller to * perform any necessary transfer negotiation. */ PROBE_SET_ACTION(softc, PROBE_TUR_FOR_NEGOTIATION); xpt_schedule(periph, priority); goto out; } xpt_release_ccb(done_ccb); break; } case PROBE_TUR_FOR_NEGOTIATION: case PROBE_DV_EXIT: if (cam_ccb_status(done_ccb) != CAM_REQ_CMP) { cam_periph_error(done_ccb, 0, SF_NO_PRINT | SF_NO_RECOVERY | SF_NO_RETRY, NULL); } if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* * Do Domain Validation for lun 0 on devices that claim * to support Synchronous Transfer modes. */ if (softc->action == PROBE_TUR_FOR_NEGOTIATION && done_ccb->ccb_h.target_lun == 0 && (path->device->inq_data.flags & SID_Sync) != 0 && (path->device->flags & CAM_DEV_IN_DV) == 0) { CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Begin Domain Validation\n")); path->device->flags |= CAM_DEV_IN_DV; xpt_release_ccb(done_ccb); PROBE_SET_ACTION(softc, PROBE_INQUIRY_BASIC_DV1); xpt_schedule(periph, priority); goto out; } if (softc->action == PROBE_DV_EXIT) { CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Leave Domain Validation\n")); } if (path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); } path->device->flags &= ~(CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM); if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) { /* Inform the XPT that a new device has been found */ done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); } PROBE_SET_ACTION(softc, PROBE_DONE); xpt_release_ccb(done_ccb); break; case PROBE_INQUIRY_BASIC_DV1: case PROBE_INQUIRY_BASIC_DV2: { struct scsi_inquiry_data *nbuf; struct ccb_scsiio *csio; if (cam_ccb_status(done_ccb) != CAM_REQ_CMP) { cam_periph_error(done_ccb, 0, SF_NO_PRINT | SF_NO_RECOVERY | SF_NO_RETRY, NULL); } if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } csio = &done_ccb->csio; nbuf = (struct scsi_inquiry_data *)csio->data_ptr; if (bcmp(nbuf, &path->device->inq_data, SHORT_INQUIRY_LENGTH)) { xpt_print(path, "inquiry data fails comparison at DV%d step\n", softc->action == PROBE_INQUIRY_BASIC_DV1 ? 1 : 2); if (proberequestbackoff(periph, path->device)) { path->device->flags &= ~CAM_DEV_IN_DV; PROBE_SET_ACTION(softc, PROBE_TUR_FOR_NEGOTIATION); } else { /* give up */ PROBE_SET_ACTION(softc, PROBE_DV_EXIT); } free(nbuf, M_CAMXPT); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); goto out; } free(nbuf, M_CAMXPT); if (softc->action == PROBE_INQUIRY_BASIC_DV1) { PROBE_SET_ACTION(softc, PROBE_INQUIRY_BASIC_DV2); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); goto out; } if (softc->action == PROBE_INQUIRY_BASIC_DV2) { CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Leave Domain Validation Successfully\n")); } if (path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); } path->device->flags &= ~(CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM); if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) { /* Inform the XPT that a new device has been found */ done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); } PROBE_SET_ACTION(softc, PROBE_DONE); xpt_release_ccb(done_ccb); break; } default: panic("probedone: invalid action state 0x%x\n", softc->action); } done_ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); TAILQ_REMOVE(&softc->request_ccbs, &done_ccb->ccb_h, periph_links.tqe); done_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(done_ccb); if (TAILQ_FIRST(&softc->request_ccbs) == NULL) { CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Probe completed\n")); /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */ cam_release_devq(path, 0, 0, 0, FALSE); cam_periph_release_locked(periph); cam_periph_invalidate(periph); cam_periph_release_locked(periph); } else { probeschedule(periph); goto out; } } static void probe_purge_old(struct cam_path *path, struct scsi_report_luns_data *new, probe_flags flags) { struct cam_path *tp; struct scsi_report_luns_data *old; u_int idx1, idx2, nlun_old, nlun_new; lun_id_t this_lun; u_int8_t *ol, *nl; if (path->target == NULL) { return; } mtx_lock(&path->target->luns_mtx); old = path->target->luns; path->target->luns = new; mtx_unlock(&path->target->luns_mtx); if (old == NULL) return; nlun_old = scsi_4btoul(old->length) / 8; nlun_new = scsi_4btoul(new->length) / 8; /* * We are not going to assume sorted lists. Deal. */ for (idx1 = 0; idx1 < nlun_old; idx1++) { ol = old->luns[idx1].lundata; for (idx2 = 0; idx2 < nlun_new; idx2++) { nl = new->luns[idx2].lundata; if (memcmp(nl, ol, 8) == 0) { break; } } if (idx2 < nlun_new) { continue; } /* * An 'old' item not in the 'new' list. * Nuke it. Except that if it is lun 0, * that would be what the probe state * machine is currently working on, * so we won't do that. */ CAM_GET_LUN(old, idx1, this_lun); if (this_lun == 0) { continue; } /* * We also cannot nuke it if it is * not in a lun format we understand * and replace the LUN with a "simple" LUN * if that is all the HBA supports. */ if (!(flags & PROBE_EXTLUN)) { if (!CAM_CAN_GET_SIMPLE_LUN(old, idx1)) continue; CAM_GET_SIMPLE_LUN(old, idx1, this_lun); } if (xpt_create_path(&tp, NULL, xpt_path_path_id(path), xpt_path_target_id(path), this_lun) == CAM_REQ_CMP) { xpt_async(AC_LOST_DEVICE, tp, NULL); xpt_free_path(tp); } } free(old, M_CAMXPT); } static void probecleanup(struct cam_periph *periph) { free(periph->softc, M_CAMXPT); } static void scsi_find_quirk(struct cam_ed *device) { struct scsi_quirk_entry *quirk; caddr_t match; match = cam_quirkmatch((caddr_t)&device->inq_data, (caddr_t)scsi_quirk_table, sizeof(scsi_quirk_table) / sizeof(*scsi_quirk_table), sizeof(*scsi_quirk_table), scsi_inquiry_match); if (match == NULL) panic("xpt_find_quirk: device didn't match wildcard entry!!"); quirk = (struct scsi_quirk_entry *)match; device->quirk = quirk; device->mintags = quirk->mintags; device->maxtags = quirk->maxtags; } static int sysctl_cam_search_luns(SYSCTL_HANDLER_ARGS) { int error, val; val = cam_srch_hi; error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (val == 0 || val == 1) { cam_srch_hi = val; return (0); } else { return (EINVAL); } } typedef struct { union ccb *request_ccb; struct ccb_pathinq *cpi; int counter; int lunindex[0]; } scsi_scan_bus_info; /* * To start a scan, request_ccb is an XPT_SCAN_BUS ccb. * As the scan progresses, scsi_scan_bus is used as the * callback on completion function. */ static void scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb) { struct mtx *mtx; CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("scsi_scan_bus\n")); switch (request_ccb->ccb_h.func_code) { case XPT_SCAN_BUS: case XPT_SCAN_TGT: { scsi_scan_bus_info *scan_info; union ccb *work_ccb, *reset_ccb; struct cam_path *path; u_int i; u_int low_target, max_target; u_int initiator_id; /* Find out the characteristics of the bus */ work_ccb = xpt_alloc_ccb_nowait(); if (work_ccb == NULL) { request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(request_ccb); return; } xpt_setup_ccb(&work_ccb->ccb_h, request_ccb->ccb_h.path, request_ccb->ccb_h.pinfo.priority); work_ccb->ccb_h.func_code = XPT_PATH_INQ; xpt_action(work_ccb); if (work_ccb->ccb_h.status != CAM_REQ_CMP) { request_ccb->ccb_h.status = work_ccb->ccb_h.status; xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } if ((work_ccb->cpi.hba_misc & PIM_NOINITIATOR) != 0) { /* * Can't scan the bus on an adapter that * cannot perform the initiator role. */ request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } /* We may need to reset bus first, if we haven't done it yet. */ if ((work_ccb->cpi.hba_inquiry & (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) && !(work_ccb->cpi.hba_misc & PIM_NOBUSRESET) && !timevalisset(&request_ccb->ccb_h.path->bus->last_reset) && (reset_ccb = xpt_alloc_ccb_nowait()) != NULL) { xpt_setup_ccb(&reset_ccb->ccb_h, request_ccb->ccb_h.path, CAM_PRIORITY_NONE); reset_ccb->ccb_h.func_code = XPT_RESET_BUS; xpt_action(reset_ccb); if (reset_ccb->ccb_h.status != CAM_REQ_CMP) { request_ccb->ccb_h.status = reset_ccb->ccb_h.status; xpt_free_ccb(reset_ccb); xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } xpt_free_ccb(reset_ccb); } /* Save some state for use while we probe for devices */ scan_info = (scsi_scan_bus_info *) malloc(sizeof(scsi_scan_bus_info) + (work_ccb->cpi.max_target * sizeof (u_int)), M_CAMXPT, M_ZERO|M_NOWAIT); if (scan_info == NULL) { request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("SCAN start for %p\n", scan_info)); scan_info->request_ccb = request_ccb; scan_info->cpi = &work_ccb->cpi; /* Cache on our stack so we can work asynchronously */ max_target = scan_info->cpi->max_target; low_target = 0; initiator_id = scan_info->cpi->initiator_id; /* * We can scan all targets in parallel, or do it sequentially. */ if (request_ccb->ccb_h.func_code == XPT_SCAN_TGT) { max_target = low_target = request_ccb->ccb_h.target_id; scan_info->counter = 0; } else if (scan_info->cpi->hba_misc & PIM_SEQSCAN) { max_target = 0; scan_info->counter = 0; } else { scan_info->counter = scan_info->cpi->max_target + 1; if (scan_info->cpi->initiator_id < scan_info->counter) { scan_info->counter--; } } mtx = xpt_path_mtx(scan_info->request_ccb->ccb_h.path); mtx_unlock(mtx); for (i = low_target; i <= max_target; i++) { cam_status status; if (i == initiator_id) continue; status = xpt_create_path(&path, NULL, request_ccb->ccb_h.path_id, i, 0); if (status != CAM_REQ_CMP) { printf("scsi_scan_bus: xpt_create_path failed" " with status %#x, bus scan halted\n", status); free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = status; xpt_free_ccb(work_ccb); xpt_done(request_ccb); break; } work_ccb = xpt_alloc_ccb_nowait(); if (work_ccb == NULL) { xpt_free_ccb((union ccb *)scan_info->cpi); free(scan_info, M_CAMXPT); xpt_free_path(path); request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(request_ccb); break; } xpt_setup_ccb(&work_ccb->ccb_h, path, request_ccb->ccb_h.pinfo.priority); work_ccb->ccb_h.func_code = XPT_SCAN_LUN; work_ccb->ccb_h.cbfcnp = scsi_scan_bus; work_ccb->ccb_h.flags |= CAM_UNLOCKED; work_ccb->ccb_h.ppriv_ptr0 = scan_info; work_ccb->crcn.flags = request_ccb->crcn.flags; xpt_action(work_ccb); } mtx_lock(mtx); break; } case XPT_SCAN_LUN: { cam_status status; struct cam_path *path, *oldpath; scsi_scan_bus_info *scan_info; struct cam_et *target; struct cam_ed *device, *nextdev; int next_target; path_id_t path_id; target_id_t target_id; lun_id_t lun_id; oldpath = request_ccb->ccb_h.path; status = cam_ccb_status(request_ccb); scan_info = (scsi_scan_bus_info *)request_ccb->ccb_h.ppriv_ptr0; path_id = request_ccb->ccb_h.path_id; target_id = request_ccb->ccb_h.target_id; lun_id = request_ccb->ccb_h.target_lun; target = request_ccb->ccb_h.path->target; next_target = 1; mtx = xpt_path_mtx(scan_info->request_ccb->ccb_h.path); mtx_lock(mtx); mtx_lock(&target->luns_mtx); if (target->luns) { lun_id_t first; u_int nluns = scsi_4btoul(target->luns->length) / 8; /* * Make sure we skip over lun 0 if it's the first member * of the list as we've actually just finished probing * it. */ CAM_GET_LUN(target->luns, 0, first); if (first == 0 && scan_info->lunindex[target_id] == 0) { scan_info->lunindex[target_id]++; } /* * Skip any LUNs that the HBA can't deal with. */ while (scan_info->lunindex[target_id] < nluns) { if (scan_info->cpi->hba_misc & PIM_EXTLUNS) { CAM_GET_LUN(target->luns, scan_info->lunindex[target_id], lun_id); break; } if (CAM_CAN_GET_SIMPLE_LUN(target->luns, scan_info->lunindex[target_id])) { CAM_GET_SIMPLE_LUN(target->luns, scan_info->lunindex[target_id], lun_id); break; } scan_info->lunindex[target_id]++; } if (scan_info->lunindex[target_id] < nluns) { mtx_unlock(&target->luns_mtx); next_target = 0; CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("next lun to try at index %u is %jx\n", scan_info->lunindex[target_id], (uintmax_t)lun_id)); scan_info->lunindex[target_id]++; } else { mtx_unlock(&target->luns_mtx); /* We're done with scanning all luns. */ } } else { mtx_unlock(&target->luns_mtx); device = request_ccb->ccb_h.path->device; /* Continue sequential LUN scan if: */ /* -- we have more LUNs that need recheck */ mtx_lock(&target->bus->eb_mtx); nextdev = device; while ((nextdev = TAILQ_NEXT(nextdev, links)) != NULL) if ((nextdev->flags & CAM_DEV_UNCONFIGURED) == 0) break; mtx_unlock(&target->bus->eb_mtx); if (nextdev != NULL) { next_target = 0; /* -- stop if CAM_QUIRK_NOLUNS is set. */ } else if (SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOLUNS) { next_target = 1; /* -- this LUN is connected and its SCSI version * allows more LUNs. */ } else if ((device->flags & CAM_DEV_UNCONFIGURED) == 0) { if (lun_id < (CAM_SCSI2_MAXLUN-1) || CAN_SRCH_HI_DENSE(device)) next_target = 0; /* -- this LUN is disconnected, its SCSI version * allows more LUNs and we guess they may be. */ } else if ((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0) { if (lun_id < (CAM_SCSI2_MAXLUN-1) || CAN_SRCH_HI_SPARSE(device)) next_target = 0; } if (next_target == 0) { lun_id++; if (lun_id > scan_info->cpi->max_lun) next_target = 1; } } /* * Check to see if we scan any further luns. */ if (next_target) { int done; /* * Free the current request path- we're done with it. */ xpt_free_path(oldpath); hop_again: done = 0; if (scan_info->request_ccb->ccb_h.func_code == XPT_SCAN_TGT) { done = 1; } else if (scan_info->cpi->hba_misc & PIM_SEQSCAN) { scan_info->counter++; if (scan_info->counter == scan_info->cpi->initiator_id) { scan_info->counter++; } if (scan_info->counter >= scan_info->cpi->max_target+1) { done = 1; } } else { scan_info->counter--; if (scan_info->counter == 0) { done = 1; } } if (done) { mtx_unlock(mtx); xpt_free_ccb(request_ccb); xpt_free_ccb((union ccb *)scan_info->cpi); request_ccb = scan_info->request_ccb; CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("SCAN done for %p\n", scan_info)); free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(request_ccb); break; } if ((scan_info->cpi->hba_misc & PIM_SEQSCAN) == 0) { mtx_unlock(mtx); xpt_free_ccb(request_ccb); break; } status = xpt_create_path(&path, NULL, scan_info->request_ccb->ccb_h.path_id, scan_info->counter, 0); if (status != CAM_REQ_CMP) { mtx_unlock(mtx); printf("scsi_scan_bus: xpt_create_path failed" " with status %#x, bus scan halted\n", status); xpt_free_ccb(request_ccb); xpt_free_ccb((union ccb *)scan_info->cpi); request_ccb = scan_info->request_ccb; free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = status; xpt_done(request_ccb); break; } xpt_setup_ccb(&request_ccb->ccb_h, path, request_ccb->ccb_h.pinfo.priority); request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->ccb_h.cbfcnp = scsi_scan_bus; request_ccb->ccb_h.flags |= CAM_UNLOCKED; request_ccb->ccb_h.ppriv_ptr0 = scan_info; request_ccb->crcn.flags = scan_info->request_ccb->crcn.flags; } else { status = xpt_create_path(&path, NULL, path_id, target_id, lun_id); /* * Free the old request path- we're done with it. We * do this *after* creating the new path so that * we don't remove a target that has our lun list * in the case that lun 0 is not present. */ xpt_free_path(oldpath); if (status != CAM_REQ_CMP) { printf("scsi_scan_bus: xpt_create_path failed " "with status %#x, halting LUN scan\n", status); goto hop_again; } xpt_setup_ccb(&request_ccb->ccb_h, path, request_ccb->ccb_h.pinfo.priority); request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->ccb_h.cbfcnp = scsi_scan_bus; request_ccb->ccb_h.flags |= CAM_UNLOCKED; request_ccb->ccb_h.ppriv_ptr0 = scan_info; request_ccb->crcn.flags = scan_info->request_ccb->crcn.flags; } mtx_unlock(mtx); xpt_action(request_ccb); break; } default: break; } } static void scsi_scan_lun(struct cam_periph *periph, struct cam_path *path, cam_flags flags, union ccb *request_ccb) { struct ccb_pathinq cpi; cam_status status; struct cam_path *new_path; struct cam_periph *old_periph; int lock; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("scsi_scan_lun\n")); xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status != CAM_REQ_CMP) { if (request_ccb != NULL) { request_ccb->ccb_h.status = cpi.ccb_h.status; xpt_done(request_ccb); } return; } if ((cpi.hba_misc & PIM_NOINITIATOR) != 0) { /* * Can't scan the bus on an adapter that * cannot perform the initiator role. */ if (request_ccb != NULL) { request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(request_ccb); } return; } if (request_ccb == NULL) { request_ccb = xpt_alloc_ccb_nowait(); if (request_ccb == NULL) { xpt_print(path, "scsi_scan_lun: can't allocate CCB, " "can't continue\n"); return; } status = xpt_create_path(&new_path, NULL, path->bus->path_id, path->target->target_id, path->device->lun_id); if (status != CAM_REQ_CMP) { xpt_print(path, "scsi_scan_lun: can't create path, " "can't continue\n"); xpt_free_ccb(request_ccb); return; } xpt_setup_ccb(&request_ccb->ccb_h, new_path, CAM_PRIORITY_XPT); request_ccb->ccb_h.cbfcnp = xptscandone; request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->ccb_h.flags |= CAM_UNLOCKED; request_ccb->crcn.flags = flags; } lock = (xpt_path_owned(path) == 0); if (lock) xpt_path_lock(path); if ((old_periph = cam_periph_find(path, "probe")) != NULL) { if ((old_periph->flags & CAM_PERIPH_INVALID) == 0) { probe_softc *softc; softc = (probe_softc *)old_periph->softc; TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, periph_links.tqe); } else { request_ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(request_ccb); } } else { status = cam_periph_alloc(proberegister, NULL, probecleanup, probestart, "probe", CAM_PERIPH_BIO, request_ccb->ccb_h.path, NULL, 0, request_ccb); if (status != CAM_REQ_CMP) { xpt_print(path, "scsi_scan_lun: cam_alloc_periph " "returned an error, can't continue probe\n"); request_ccb->ccb_h.status = status; xpt_done(request_ccb); } } if (lock) xpt_path_unlock(path); } static void xptscandone(struct cam_periph *periph, union ccb *done_ccb) { xpt_free_path(done_ccb->ccb_h.path); xpt_free_ccb(done_ccb); } static struct cam_ed * scsi_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) { struct scsi_quirk_entry *quirk; struct cam_ed *device; device = xpt_alloc_device(bus, target, lun_id); if (device == NULL) return (NULL); /* * Take the default quirk entry until we have inquiry * data and can determine a better quirk to use. */ quirk = &scsi_quirk_table[scsi_quirk_table_size - 1]; device->quirk = (void *)quirk; device->mintags = quirk->mintags; device->maxtags = quirk->maxtags; bzero(&device->inq_data, sizeof(device->inq_data)); device->inq_flags = 0; device->queue_flags = 0; device->serial_num = NULL; device->serial_num_len = 0; device->device_id = NULL; device->device_id_len = 0; device->supported_vpds = NULL; device->supported_vpds_len = 0; return (device); } static void scsi_devise_transport(struct cam_path *path) { struct ccb_pathinq cpi; struct ccb_trans_settings cts; struct scsi_inquiry_data *inq_buf; /* Get transport information from the SIM */ xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); inq_buf = NULL; if ((path->device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0) inq_buf = &path->device->inq_data; path->device->protocol = PROTO_SCSI; path->device->protocol_version = inq_buf != NULL ? SID_ANSI_REV(inq_buf) : cpi.protocol_version; path->device->transport = cpi.transport; path->device->transport_version = cpi.transport_version; /* * Any device not using SPI3 features should * be considered SPI2 or lower. */ if (inq_buf != NULL) { if (path->device->transport == XPORT_SPI && (inq_buf->spi3data & SID_SPI_MASK) == 0 && path->device->transport_version > 2) path->device->transport_version = 2; } else { struct cam_ed* otherdev; for (otherdev = TAILQ_FIRST(&path->target->ed_entries); otherdev != NULL; otherdev = TAILQ_NEXT(otherdev, links)) { if (otherdev != path->device) break; } if (otherdev != NULL) { /* * Initially assume the same versioning as * prior luns for this target. */ path->device->protocol_version = otherdev->protocol_version; path->device->transport_version = otherdev->transport_version; } else { /* Until we know better, opt for safty */ path->device->protocol_version = 2; if (path->device->transport == XPORT_SPI) path->device->transport_version = 2; else path->device->transport_version = 0; } } /* * XXX * For a device compliant with SPC-2 we should be able * to determine the transport version supported by * scrutinizing the version descriptors in the * inquiry buffer. */ /* Tell the controller what we think */ xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.transport = path->device->transport; cts.transport_version = path->device->transport_version; cts.protocol = path->device->protocol; cts.protocol_version = path->device->protocol_version; cts.proto_specific.valid = 0; cts.xport_specific.valid = 0; xpt_action((union ccb *)&cts); } static void scsi_dev_advinfo(union ccb *start_ccb) { struct cam_ed *device; struct ccb_dev_advinfo *cdai; off_t amt; start_ccb->ccb_h.status = CAM_REQ_INVALID; device = start_ccb->ccb_h.path->device; cdai = &start_ccb->cdai; switch(cdai->buftype) { case CDAI_TYPE_SCSI_DEVID: if (cdai->flags & CDAI_FLAG_STORE) return; cdai->provsiz = device->device_id_len; if (device->device_id_len == 0) break; amt = device->device_id_len; if (cdai->provsiz > cdai->bufsiz) amt = cdai->bufsiz; memcpy(cdai->buf, device->device_id, amt); break; case CDAI_TYPE_SERIAL_NUM: if (cdai->flags & CDAI_FLAG_STORE) return; cdai->provsiz = device->serial_num_len; if (device->serial_num_len == 0) break; amt = device->serial_num_len; if (cdai->provsiz > cdai->bufsiz) amt = cdai->bufsiz; memcpy(cdai->buf, device->serial_num, amt); break; case CDAI_TYPE_PHYS_PATH: if (cdai->flags & CDAI_FLAG_STORE) { if (device->physpath != NULL) { free(device->physpath, M_CAMXPT); device->physpath = NULL; } device->physpath_len = cdai->bufsiz; /* Clear existing buffer if zero length */ if (cdai->bufsiz == 0) break; device->physpath = malloc(cdai->bufsiz, M_CAMXPT, M_NOWAIT); if (device->physpath == NULL) { start_ccb->ccb_h.status = CAM_REQ_ABORTED; return; } memcpy(device->physpath, cdai->buf, cdai->bufsiz); } else { cdai->provsiz = device->physpath_len; if (device->physpath_len == 0) break; amt = device->physpath_len; if (cdai->provsiz > cdai->bufsiz) amt = cdai->bufsiz; memcpy(cdai->buf, device->physpath, amt); } break; case CDAI_TYPE_RCAPLONG: if (cdai->flags & CDAI_FLAG_STORE) { if (device->rcap_buf != NULL) { free(device->rcap_buf, M_CAMXPT); device->rcap_buf = NULL; } device->rcap_len = cdai->bufsiz; /* Clear existing buffer if zero length */ if (cdai->bufsiz == 0) break; device->rcap_buf = malloc(cdai->bufsiz, M_CAMXPT, M_NOWAIT); if (device->rcap_buf == NULL) { start_ccb->ccb_h.status = CAM_REQ_ABORTED; return; } memcpy(device->rcap_buf, cdai->buf, cdai->bufsiz); } else { cdai->provsiz = device->rcap_len; if (device->rcap_len == 0) break; amt = device->rcap_len; if (cdai->provsiz > cdai->bufsiz) amt = cdai->bufsiz; memcpy(cdai->buf, device->rcap_buf, amt); } break; case CDAI_TYPE_EXT_INQ: /* * We fetch extended inquiry data during probe, if * available. We don't allow changing it. */ if (cdai->flags & CDAI_FLAG_STORE) return; cdai->provsiz = device->ext_inq_len; if (device->ext_inq_len == 0) break; amt = device->ext_inq_len; if (cdai->provsiz > cdai->bufsiz) amt = cdai->bufsiz; memcpy(cdai->buf, device->ext_inq, amt); break; default: return; } start_ccb->ccb_h.status = CAM_REQ_CMP; if (cdai->flags & CDAI_FLAG_STORE) { xpt_async(AC_ADVINFO_CHANGED, start_ccb->ccb_h.path, (void *)(uintptr_t)cdai->buftype); } } static void scsi_action(union ccb *start_ccb) { switch (start_ccb->ccb_h.func_code) { case XPT_SET_TRAN_SETTINGS: { scsi_set_transfer_settings(&start_ccb->cts, start_ccb->ccb_h.path, /*async_update*/FALSE); break; } case XPT_SCAN_BUS: case XPT_SCAN_TGT: scsi_scan_bus(start_ccb->ccb_h.path->periph, start_ccb); break; case XPT_SCAN_LUN: scsi_scan_lun(start_ccb->ccb_h.path->periph, start_ccb->ccb_h.path, start_ccb->crcn.flags, start_ccb); break; case XPT_DEV_ADVINFO: { scsi_dev_advinfo(start_ccb); break; } default: xpt_action_default(start_ccb); break; } } static void scsi_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_path *path, int async_update) { struct ccb_pathinq cpi; struct ccb_trans_settings cur_cts; struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_scsi *cur_scsi; struct scsi_inquiry_data *inq_data; struct cam_ed *device; if (path == NULL || (device = path->device) == NULL) { cts->ccb_h.status = CAM_PATH_INVALID; xpt_done((union ccb *)cts); return; } if (cts->protocol == PROTO_UNKNOWN || cts->protocol == PROTO_UNSPECIFIED) { cts->protocol = device->protocol; cts->protocol_version = device->protocol_version; } if (cts->protocol_version == PROTO_VERSION_UNKNOWN || cts->protocol_version == PROTO_VERSION_UNSPECIFIED) cts->protocol_version = device->protocol_version; if (cts->protocol != device->protocol) { xpt_print(path, "Uninitialized Protocol %x:%x?\n", cts->protocol, device->protocol); cts->protocol = device->protocol; } if (cts->protocol_version > device->protocol_version) { if (bootverbose) { xpt_print(path, "Down reving Protocol " "Version from %d to %d?\n", cts->protocol_version, device->protocol_version); } cts->protocol_version = device->protocol_version; } if (cts->transport == XPORT_UNKNOWN || cts->transport == XPORT_UNSPECIFIED) { cts->transport = device->transport; cts->transport_version = device->transport_version; } if (cts->transport_version == XPORT_VERSION_UNKNOWN || cts->transport_version == XPORT_VERSION_UNSPECIFIED) cts->transport_version = device->transport_version; if (cts->transport != device->transport) { xpt_print(path, "Uninitialized Transport %x:%x?\n", cts->transport, device->transport); cts->transport = device->transport; } if (cts->transport_version > device->transport_version) { if (bootverbose) { xpt_print(path, "Down reving Transport " "Version from %d to %d?\n", cts->transport_version, device->transport_version); } cts->transport_version = device->transport_version; } /* * Nothing more of interest to do unless * this is a device connected via the * SCSI protocol. */ if (cts->protocol != PROTO_SCSI) { if (async_update == FALSE) xpt_action_default((union ccb *)cts); return; } inq_data = &device->inq_data; scsi = &cts->proto_specific.scsi; xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); /* SCSI specific sanity checking */ if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0 || (INQ_DATA_TQ_ENABLED(inq_data)) == 0 || (device->queue_flags & SCP_QUEUE_DQUE) != 0 || (device->mintags == 0)) { /* * Can't tag on hardware that doesn't support tags, * doesn't have it enabled, or has broken tag support. */ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; } if (async_update == FALSE) { /* * Perform sanity checking against what the * controller and device can do. */ xpt_setup_ccb(&cur_cts.ccb_h, path, CAM_PRIORITY_NONE); cur_cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cur_cts.type = cts->type; xpt_action((union ccb *)&cur_cts); if (cam_ccb_status((union ccb *)&cur_cts) != CAM_REQ_CMP) { return; } cur_scsi = &cur_cts.proto_specific.scsi; if ((scsi->valid & CTS_SCSI_VALID_TQ) == 0) { scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->flags |= cur_scsi->flags & CTS_SCSI_FLAGS_TAG_ENB; } if ((cur_scsi->valid & CTS_SCSI_VALID_TQ) == 0) scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; } /* SPI specific sanity checking */ if (cts->transport == XPORT_SPI && async_update == FALSE) { u_int spi3caps; struct ccb_trans_settings_spi *spi; struct ccb_trans_settings_spi *cur_spi; spi = &cts->xport_specific.spi; cur_spi = &cur_cts.xport_specific.spi; /* Fill in any gaps in what the user gave us */ if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) spi->sync_period = cur_spi->sync_period; if ((cur_spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) spi->sync_period = 0; if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) spi->sync_offset = cur_spi->sync_offset; if ((cur_spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) spi->sync_offset = 0; if ((spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) spi->ppr_options = cur_spi->ppr_options; if ((cur_spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) spi->ppr_options = 0; if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) == 0) spi->bus_width = cur_spi->bus_width; if ((cur_spi->valid & CTS_SPI_VALID_BUS_WIDTH) == 0) spi->bus_width = 0; if ((spi->valid & CTS_SPI_VALID_DISC) == 0) { spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; spi->flags |= cur_spi->flags & CTS_SPI_FLAGS_DISC_ENB; } if ((cur_spi->valid & CTS_SPI_VALID_DISC) == 0) spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0 && (inq_data->flags & SID_Sync) == 0 && cts->type == CTS_TYPE_CURRENT_SETTINGS) || ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0)) { /* Force async */ spi->sync_period = 0; spi->sync_offset = 0; } switch (spi->bus_width) { case MSG_EXT_WDTR_BUS_32_BIT: if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0 || (inq_data->flags & SID_WBus32) != 0 || cts->type == CTS_TYPE_USER_SETTINGS) && (cpi.hba_inquiry & PI_WIDE_32) != 0) break; /* Fall Through to 16-bit */ case MSG_EXT_WDTR_BUS_16_BIT: if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0 || (inq_data->flags & SID_WBus16) != 0 || cts->type == CTS_TYPE_USER_SETTINGS) && (cpi.hba_inquiry & PI_WIDE_16) != 0) { spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; break; } /* Fall Through to 8-bit */ default: /* New bus width?? */ case MSG_EXT_WDTR_BUS_8_BIT: /* All targets can do this */ spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } spi3caps = cpi.xport_specific.spi.ppr_options; if ((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0 && cts->type == CTS_TYPE_CURRENT_SETTINGS) spi3caps &= inq_data->spi3data; if ((spi3caps & SID_SPI_CLOCK_DT) == 0) spi->ppr_options &= ~MSG_EXT_PPR_DT_REQ; if ((spi3caps & SID_SPI_IUS) == 0) spi->ppr_options &= ~MSG_EXT_PPR_IU_REQ; if ((spi3caps & SID_SPI_QAS) == 0) spi->ppr_options &= ~MSG_EXT_PPR_QAS_REQ; /* No SPI Transfer settings are allowed unless we are wide */ if (spi->bus_width == 0) spi->ppr_options = 0; if ((spi->valid & CTS_SPI_VALID_DISC) && ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) == 0)) { /* * Can't tag queue without disconnection. */ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->valid |= CTS_SCSI_VALID_TQ; } /* * If we are currently performing tagged transactions to * this device and want to change its negotiation parameters, * go non-tagged for a bit to give the controller a chance to * negotiate unhampered by tag messages. */ if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (device->inq_flags & SID_CmdQue) != 0 && (scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0 && (spi->flags & (CTS_SPI_VALID_SYNC_RATE| CTS_SPI_VALID_SYNC_OFFSET| CTS_SPI_VALID_BUS_WIDTH)) != 0) scsi_toggle_tags(path); } if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (scsi->valid & CTS_SCSI_VALID_TQ) != 0) { int device_tagenb; /* * If we are transitioning from tags to no-tags or * vice-versa, we need to carefully freeze and restart * the queue so that we don't overlap tagged and non-tagged * commands. We also temporarily stop tags if there is * a change in transfer negotiation settings to allow * "tag-less" negotiation. */ if ((device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || (device->inq_flags & SID_CmdQue) != 0) device_tagenb = TRUE; else device_tagenb = FALSE; if (((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0 && device_tagenb == FALSE) || ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) == 0 && device_tagenb == TRUE)) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) { /* * Delay change to use tags until after a * few commands have gone to this device so * the controller has time to perform transfer * negotiations without tagged messages getting * in the way. */ device->tag_delay_count = CAM_TAG_DELAY_COUNT; device->flags |= CAM_DEV_TAG_AFTER_COUNT; } else { xpt_stop_tags(path); } } } if (async_update == FALSE) xpt_action_default((union ccb *)cts); } static void scsi_toggle_tags(struct cam_path *path) { struct cam_ed *dev; /* * Give controllers a chance to renegotiate * before starting tag operations. We * "toggle" tagged queuing off then on * which causes the tag enable command delay * counter to come into effect. */ dev = path->device; if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || ((dev->inq_flags & SID_CmdQue) != 0 && (dev->inq_flags & (SID_Sync|SID_WBus16|SID_WBus32)) != 0)) { struct ccb_trans_settings cts; xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.protocol = PROTO_SCSI; cts.protocol_version = PROTO_VERSION_UNSPECIFIED; cts.transport = XPORT_UNSPECIFIED; cts.transport_version = XPORT_VERSION_UNSPECIFIED; cts.proto_specific.scsi.flags = 0; cts.proto_specific.scsi.valid = CTS_SCSI_VALID_TQ; scsi_set_transfer_settings(&cts, path, /*async_update*/TRUE); cts.proto_specific.scsi.flags = CTS_SCSI_FLAGS_TAG_ENB; scsi_set_transfer_settings(&cts, path, /*async_update*/TRUE); } } /* * Handle any per-device event notifications that require action by the XPT. */ static void scsi_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg) { cam_status status; struct cam_path newpath; /* * We only need to handle events for real devices. */ if (target->target_id == CAM_TARGET_WILDCARD || device->lun_id == CAM_LUN_WILDCARD) return; /* * We need our own path with wildcards expanded to * handle certain types of events. */ if ((async_code == AC_SENT_BDR) || (async_code == AC_BUS_RESET) || (async_code == AC_INQ_CHANGED)) status = xpt_compile_path(&newpath, NULL, bus->path_id, target->target_id, device->lun_id); else status = CAM_REQ_CMP_ERR; if (status == CAM_REQ_CMP) { /* * Allow transfer negotiation to occur in a * tag free environment and after settle delay. */ if (async_code == AC_SENT_BDR || async_code == AC_BUS_RESET) { cam_freeze_devq(&newpath); cam_release_devq(&newpath, RELSIM_RELEASE_AFTER_TIMEOUT, /*reduction*/0, /*timeout*/scsi_delay, /*getcount_only*/0); scsi_toggle_tags(&newpath); } if (async_code == AC_INQ_CHANGED) { /* * We've sent a start unit command, or * something similar to a device that * may have caused its inquiry data to * change. So we re-scan the device to * refresh the inquiry data for it. */ scsi_scan_lun(newpath.periph, &newpath, CAM_EXPECT_INQ_CHANGE, NULL); } xpt_release_path(&newpath); } else if (async_code == AC_LOST_DEVICE && (device->flags & CAM_DEV_UNCONFIGURED) == 0) { device->flags |= CAM_DEV_UNCONFIGURED; xpt_release_device(device); } else if (async_code == AC_TRANSFER_NEG) { struct ccb_trans_settings *settings; struct cam_path path; settings = (struct ccb_trans_settings *)async_arg; xpt_compile_path(&path, NULL, bus->path_id, target->target_id, device->lun_id); scsi_set_transfer_settings(settings, &path, /*async_update*/TRUE); xpt_release_path(&path); } } static void scsi_announce_periph(struct cam_periph *periph) { struct ccb_pathinq cpi; struct ccb_trans_settings cts; struct cam_path *path = periph->path; u_int speed; u_int freq; u_int mb; cam_periph_assert(periph, MA_OWNED); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NORMAL); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb*)&cts); if (cam_ccb_status((union ccb *)&cts) != CAM_REQ_CMP) return; /* Ask the SIM for its base transfer speed */ xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); /* Report connection speed */ speed = cpi.base_transfer_speed; freq = 0; if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SPI) { struct ccb_trans_settings_spi *spi = &cts.xport_specific.spi; if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0 && spi->sync_offset != 0) { freq = scsi_calc_syncsrate(spi->sync_period); speed = freq; } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) speed *= (0x01 << spi->bus_width); } if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_FC) { struct ccb_trans_settings_fc *fc = &cts.xport_specific.fc; if (fc->valid & CTS_FC_VALID_SPEED) speed = fc->bitrate; } if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SAS) { struct ccb_trans_settings_sas *sas = &cts.xport_specific.sas; if (sas->valid & CTS_SAS_VALID_SPEED) speed = sas->bitrate; } mb = speed / 1000; if (mb > 0) printf("%s%d: %d.%03dMB/s transfers", periph->periph_name, periph->unit_number, mb, speed % 1000); else printf("%s%d: %dKB/s transfers", periph->periph_name, periph->unit_number, speed); /* Report additional information about SPI connections */ if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SPI) { struct ccb_trans_settings_spi *spi; spi = &cts.xport_specific.spi; if (freq != 0) { printf(" (%d.%03dMHz%s, offset %d", freq / 1000, freq % 1000, (spi->ppr_options & MSG_EXT_PPR_DT_REQ) != 0 ? " DT" : "", spi->sync_offset); } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0 && spi->bus_width > 0) { if (freq != 0) { printf(", "); } else { printf(" ("); } printf("%dbit)", 8 * (0x01 << spi->bus_width)); } else if (freq != 0) { printf(")"); } } if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_FC) { struct ccb_trans_settings_fc *fc; fc = &cts.xport_specific.fc; if (fc->valid & CTS_FC_VALID_WWNN) printf(" WWNN 0x%llx", (long long) fc->wwnn); if (fc->valid & CTS_FC_VALID_WWPN) printf(" WWPN 0x%llx", (long long) fc->wwpn); if (fc->valid & CTS_FC_VALID_PORT) printf(" PortID 0x%x", fc->port); } printf("\n"); } Index: projects/release-pkg/sys/conf/kmod.mk =================================================================== --- projects/release-pkg/sys/conf/kmod.mk (revision 295422) +++ projects/release-pkg/sys/conf/kmod.mk (revision 295423) @@ -1,464 +1,466 @@ # From: @(#)bsd.prog.mk 5.26 (Berkeley) 6/25/91 # $FreeBSD$ # # The include file handles building and installing loadable # kernel modules. # # # +++ variables +++ # # CLEANFILES Additional files to remove for the clean and cleandir targets. # # EXPORT_SYMS A list of symbols that should be exported from the module, # or the name of a file containing a list of symbols, or YES # to export all symbols. If not defined, no symbols are # exported. # # KMOD The name of the kernel module to build. # # KMODDIR Base path for kernel modules (see kld(4)). [/boot/kernel] # # KMODOWN Module file owner. [${BINOWN}] # # KMODGRP Module file group. [${BINGRP}] # # KMODMODE Module file mode. [${BINMODE}] # # KMODLOAD Command to load a kernel module [/sbin/kldload] # # KMODUNLOAD Command to unload a kernel module [/sbin/kldunload] # # KMODISLOADED Command to check whether a kernel module is # loaded [/sbin/kldstat -q -n] # # PROG The name of the kernel module to build. # If not supplied, ${KMOD}.ko is used. # # SRCS List of source files. # # FIRMWS List of firmware images in format filename:shortname:version # # FIRMWARE_LICENSE # Set to the name of the license the user has to agree on in # order to use this firmware. See /usr/share/doc/legal # # DESTDIR The tree where the module gets installed. [not set] # # +++ targets +++ # # install: # install the kernel module; if the Makefile # does not itself define the target install, the targets # beforeinstall and afterinstall may also be used to cause # actions immediately before and after the install target # is executed. # # load: # Load a module. # # unload: # Unload a module. # # reload: # Unload if loaded, then load. # AWK?= awk KMODLOAD?= /sbin/kldload KMODUNLOAD?= /sbin/kldunload KMODISLOADED?= /sbin/kldstat -q -n OBJCOPY?= objcopy .include # Grab all the options for a kernel build. For backwards compat, we need to # do this after bsd.own.mk. .include "kern.opts.mk" .include .include "config.mk" .SUFFIXES: .out .o .c .cc .cxx .C .y .l .s .S .m # amd64 and mips use direct linking for kmod, all others use shared binaries .if ${MACHINE_CPUARCH} != amd64 && ${MACHINE_CPUARCH} != mips __KLD_SHARED=yes .else __KLD_SHARED=no .endif .if !empty(CFLAGS:M-O[23s]) && empty(CFLAGS:M-fno-strict-aliasing) CFLAGS+= -fno-strict-aliasing .endif WERROR?= -Werror CFLAGS+= ${WERROR} CFLAGS+= -D_KERNEL CFLAGS+= -DKLD_MODULE # Don't use any standard or source-relative include directories. NOSTDINC= -nostdinc CFLAGS:= ${CFLAGS:N-I*} ${NOSTDINC} ${INCLMAGIC} ${CFLAGS:M-I*} .if defined(KERNBUILDDIR) CFLAGS+= -DHAVE_KERNEL_OPTION_HEADERS -include ${KERNBUILDDIR}/opt_global.h .endif # Add -I paths for system headers. Individual module makefiles don't # need any -I paths for this. Similar defaults for .PATH can't be # set because there are no standard paths for non-headers. CFLAGS+= -I. -I${SYSDIR} CFLAGS.gcc+= -finline-limit=${INLINE_LIMIT} CFLAGS.gcc+= -fms-extensions CFLAGS.gcc+= --param inline-unit-growth=100 CFLAGS.gcc+= --param large-function-growth=1000 # Disallow common variables, and if we end up with commons from # somewhere unexpected, allocate storage for them in the module itself. CFLAGS+= -fno-common LDFLAGS+= -d -warn-common CFLAGS+= ${DEBUG_FLAGS} .if ${MACHINE_CPUARCH} == amd64 CFLAGS+= -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer .endif .if ${MACHINE_CPUARCH} == "aarch64" CFLAGS+= -fPIC .endif # Temporary workaround for PR 196407, which contains the fascinating details. # Don't allow clang to use fpu instructions or registers in kernel modules. .if ${MACHINE_CPUARCH} == arm CFLAGS.clang+= -mllvm -arm-use-movt=0 CFLAGS.clang+= -mfpu=none CFLAGS+= -funwind-tables .endif .if ${MACHINE_CPUARCH} == powerpc CFLAGS+= -mlongcall -fno-omit-frame-pointer .endif .if ${MACHINE_CPUARCH} == mips CFLAGS+= -G0 -fno-pic -mno-abicalls -mlong-calls .endif .if defined(DEBUG) || defined(DEBUG_FLAGS) CTFFLAGS+= -g .endif .if defined(FIRMWS) ${KMOD:S/$/.c/}: ${SYSDIR}/tools/fw_stub.awk ${AWK} -f ${SYSDIR}/tools/fw_stub.awk ${FIRMWS} -m${KMOD} -c${KMOD:S/$/.c/g} \ ${FIRMWARE_LICENSE:C/.+/-l/}${FIRMWARE_LICENSE} SRCS+= ${KMOD:S/$/.c/} CLEANFILES+= ${KMOD:S/$/.c/} .for _firmw in ${FIRMWS} ${_firmw:C/\:.*$/.fwo/}: ${_firmw:C/\:.*$//} @${ECHO} ${_firmw:C/\:.*$//} ${.ALLSRC:M*${_firmw:C/\:.*$//}} @if [ -e ${_firmw:C/\:.*$//} ]; then \ ${LD} -b binary --no-warn-mismatch ${_LDFLAGS} \ -r -d -o ${.TARGET} ${_firmw:C/\:.*$//}; \ else \ ln -s ${.ALLSRC:M*${_firmw:C/\:.*$//}} ${_firmw:C/\:.*$//}; \ ${LD} -b binary --no-warn-mismatch ${_LDFLAGS} \ -r -d -o ${.TARGET} ${_firmw:C/\:.*$//}; \ rm ${_firmw:C/\:.*$//}; \ fi OBJS+= ${_firmw:C/\:.*$/.fwo/} .endfor .endif # Conditionally include SRCS based on kernel config options. .for _o in ${KERN_OPTS} SRCS+=${SRCS.${_o}} .endfor OBJS+= ${SRCS:N*.h:R:S/$/.o/g} .if !defined(PROG) PROG= ${KMOD}.ko .endif .if !defined(DEBUG_FLAGS) FULLPROG= ${PROG} .else FULLPROG= ${PROG}.full ${PROG}: ${FULLPROG} ${PROG}.debug ${OBJCOPY} --strip-debug --add-gnu-debuglink=${PROG}.debug \ ${FULLPROG} ${.TARGET} ${PROG}.debug: ${FULLPROG} ${OBJCOPY} --only-keep-debug ${FULLPROG} ${.TARGET} .endif .if ${__KLD_SHARED} == yes ${FULLPROG}: ${KMOD}.kld .if ${MACHINE_CPUARCH} != "aarch64" ${LD} -Bshareable ${_LDFLAGS} -o ${.TARGET} ${KMOD}.kld .else #XXXKIB Relocatable linking in aarch64 ld from binutils 2.25.1 does # not work. The linker corrupts the references to the external # symbols which are defined by other object in the linking set # and should therefore loose the GOT entry. The problem seems # to be fixed in the binutils-gdb git HEAD as of 2015-10-04. Hack # below allows to get partially functioning modules for now. ${LD} -Bshareable ${_LDFLAGS} -o ${.TARGET} ${OBJS} .endif .if !defined(DEBUG_FLAGS) ${OBJCOPY} --strip-debug ${.TARGET} .endif .endif EXPORT_SYMS?= NO .if ${EXPORT_SYMS} != YES CLEANFILES+= export_syms .endif .if ${__KLD_SHARED} == yes ${KMOD}.kld: ${OBJS} .else ${FULLPROG}: ${OBJS} .endif ${LD} ${_LDFLAGS} -r -d -o ${.TARGET} ${OBJS} .if ${MK_CTF} != "no" ${CTFMERGE} ${CTFFLAGS} -o ${.TARGET} ${OBJS} .endif .if defined(EXPORT_SYMS) .if ${EXPORT_SYMS} != YES .if ${EXPORT_SYMS} == NO :> export_syms .elif !exists(${.CURDIR}/${EXPORT_SYMS}) echo ${EXPORT_SYMS} > export_syms .else grep -v '^#' < ${EXPORT_SYMS} > export_syms .endif ${AWK} -f ${SYSDIR}/conf/kmod_syms.awk ${.TARGET} \ export_syms | xargs -J% ${OBJCOPY} % ${.TARGET} .endif .endif .if !defined(DEBUG_FLAGS) && ${__KLD_SHARED} == no ${OBJCOPY} --strip-debug ${.TARGET} .endif _ILINKS=machine .if ${MACHINE} != ${MACHINE_CPUARCH} && ${MACHINE} != "arm64" _ILINKS+=${MACHINE_CPUARCH} .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _ILINKS+=x86 .endif CLEANFILES+=${_ILINKS} -all: objwarn ${PROG} +all: beforebuild .WAIT ${PROG} +beforebuild: objwarn beforedepend: ${_ILINKS} +beforebuild: ${_ILINKS} # Ensure that the links exist without depending on it when it exists which # causes all the modules to be rebuilt when the directory pointed to changes. .for _link in ${_ILINKS} .if !exists(${.OBJDIR}/${_link}) ${OBJS}: ${_link} .endif .endfor # Search for kernel source tree in standard places. .for _dir in ${.CURDIR}/../.. ${.CURDIR}/../../.. /sys /usr/src/sys .if !defined(SYSDIR) && exists(${_dir}/kern/) SYSDIR= ${_dir} .endif .endfor .if !defined(SYSDIR) || !exists(${SYSDIR}/kern/) .error "can't find kernel source tree" .endif .NOPATH: ${_ILINKS} ${_ILINKS}: @case ${.TARGET} in \ machine) \ path=${SYSDIR}/${MACHINE}/include ;; \ *) \ path=${SYSDIR}/${.TARGET:T}/include ;; \ esac ; \ path=`(cd $$path && /bin/pwd)` ; \ ${ECHO} ${.TARGET:T} "->" $$path ; \ ln -sf $$path ${.TARGET:T} CLEANFILES+= ${PROG} ${KMOD}.kld ${OBJS} .if defined(DEBUG_FLAGS) CLEANFILES+= ${FULLPROG} ${PROG}.debug .endif .if !target(install) _INSTALLFLAGS:= ${INSTALLFLAGS} .for ie in ${INSTALLFLAGS_EDIT} _INSTALLFLAGS:= ${_INSTALLFLAGS${ie}} .endfor .if !target(realinstall) KERN_DEBUGDIR?= ${DEBUGDIR} realinstall: _kmodinstall .ORDER: beforeinstall _kmodinstall _kmodinstall: ${INSTALL} -T release -o ${KMODOWN} -g ${KMODGRP} -m ${KMODMODE} \ ${_INSTALLFLAGS} ${PROG} ${DESTDIR}${KMODDIR}/ .if defined(DEBUG_FLAGS) && !defined(INSTALL_NODEBUG) && ${MK_KERNEL_SYMBOLS} != "no" ${INSTALL} -T debug -o ${KMODOWN} -g ${KMODGRP} -m ${KMODMODE} \ ${_INSTALLFLAGS} ${PROG}.debug ${DESTDIR}${KERN_DEBUGDIR}${KMODDIR}/ .endif .include .if !defined(NO_XREF) afterinstall: _kldxref .ORDER: realinstall _kldxref .ORDER: _installlinks _kldxref _kldxref: @if type kldxref >/dev/null 2>&1; then \ ${ECHO} kldxref ${DESTDIR}${KMODDIR}; \ kldxref ${DESTDIR}${KMODDIR}; \ fi .endif .endif # !target(realinstall) .endif # !target(install) .if !target(load) load: ${PROG} ${KMODLOAD} -v ${.OBJDIR}/${PROG} .endif .if !target(unload) unload: if ${KMODISLOADED} ${PROG} ; then ${KMODUNLOAD} -v ${PROG} ; fi .endif .if !target(reload) reload: unload load .endif .if defined(KERNBUILDDIR) .PATH: ${KERNBUILDDIR} CFLAGS+= -I${KERNBUILDDIR} .for _src in ${SRCS:Mopt_*.h} CLEANFILES+= ${_src} .if !target(${_src}) ${_src}: ln -sf ${KERNBUILDDIR}/${_src} ${.TARGET} .endif .endfor .else .for _src in ${SRCS:Mopt_*.h} CLEANFILES+= ${_src} .if !target(${_src}) ${_src}: :> ${.TARGET} .endif .endfor .endif # Respect configuration-specific C flags. CFLAGS+= ${CONF_CFLAGS} .if !empty(SRCS:Mvnode_if.c) CLEANFILES+= vnode_if.c vnode_if.c: ${SYSDIR}/tools/vnode_if.awk ${SYSDIR}/kern/vnode_if.src ${AWK} -f ${SYSDIR}/tools/vnode_if.awk ${SYSDIR}/kern/vnode_if.src -c .endif .if !empty(SRCS:Mvnode_if.h) CLEANFILES+= vnode_if.h vnode_if_newproto.h vnode_if_typedef.h vnode_if.h vnode_if_newproto.h vnode_if_typedef.h: ${SYSDIR}/tools/vnode_if.awk \ ${SYSDIR}/kern/vnode_if.src vnode_if.h: vnode_if_newproto.h vnode_if_typedef.h ${AWK} -f ${SYSDIR}/tools/vnode_if.awk ${SYSDIR}/kern/vnode_if.src -h vnode_if_newproto.h: ${AWK} -f ${SYSDIR}/tools/vnode_if.awk ${SYSDIR}/kern/vnode_if.src -p vnode_if_typedef.h: ${AWK} -f ${SYSDIR}/tools/vnode_if.awk ${SYSDIR}/kern/vnode_if.src -q .endif # Build _if.[ch] from _if.m, and clean them when we're done. # This is duplicated in sys/modules/Makefile. .if !defined(__MPATH) __MPATH!=find ${SYSDIR:tA}/ -name \*_if.m .export __MPATH .endif _MFILES=${__MPATH:T:O} _MPATH=${__MPATH:H:O:u} .PATH.m: ${_MPATH} .for _i in ${SRCS:M*_if.[ch]} _MATCH=M${_i:R:S/$/.m/} _MATCHES=${_MFILES:${_MATCH}} .if !empty(_MATCHES) CLEANFILES+= ${_i} .endif .endfor # _i .m.c: ${SYSDIR}/tools/makeobjops.awk ${AWK} -f ${SYSDIR}/tools/makeobjops.awk ${.IMPSRC} -c .m.h: ${SYSDIR}/tools/makeobjops.awk ${AWK} -f ${SYSDIR}/tools/makeobjops.awk ${.IMPSRC} -h .for _i in mii pccard .if !empty(SRCS:M${_i}devs.h) CLEANFILES+= ${_i}devs.h ${_i}devs.h: ${SYSDIR}/tools/${_i}devs2h.awk ${SYSDIR}/dev/${_i}/${_i}devs ${AWK} -f ${SYSDIR}/tools/${_i}devs2h.awk ${SYSDIR}/dev/${_i}/${_i}devs .endif .endfor # _i .if !empty(SRCS:Musbdevs.h) CLEANFILES+= usbdevs.h usbdevs.h: ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/usb/usbdevs ${AWK} -f ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/usb/usbdevs -h .endif .if !empty(SRCS:Musbdevs_data.h) CLEANFILES+= usbdevs_data.h usbdevs_data.h: ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/usb/usbdevs ${AWK} -f ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/usb/usbdevs -d .endif .if !empty(SRCS:Macpi_quirks.h) CLEANFILES+= acpi_quirks.h acpi_quirks.h: ${SYSDIR}/tools/acpi_quirks2h.awk ${SYSDIR}/dev/acpica/acpi_quirks ${AWK} -f ${SYSDIR}/tools/acpi_quirks2h.awk ${SYSDIR}/dev/acpica/acpi_quirks .endif .if !empty(SRCS:Massym.s) CLEANFILES+= assym.s genassym.o assym.s: genassym.o .if defined(KERNBUILDDIR) genassym.o: opt_global.h .endif assym.s: ${SYSDIR}/kern/genassym.sh sh ${SYSDIR}/kern/genassym.sh genassym.o > ${.TARGET} genassym.o: ${SYSDIR}/${MACHINE}/${MACHINE}/genassym.c genassym.o: ${SRCS:Mopt_*.h} ${CC} -c ${CFLAGS:N-fno-common} \ ${SYSDIR}/${MACHINE}/${MACHINE}/genassym.c .endif lint: ${SRCS} ${LINT} ${LINTKERNFLAGS} ${CFLAGS:M-[DILU]*} ${.ALLSRC:M*.c} .if defined(KERNBUILDDIR) ${OBJS}: opt_global.h .endif .include cleandepend: cleanilinks # .depend needs include links so we remove them only together. cleanilinks: rm -f ${_ILINKS} .if !exists(${.OBJDIR}/${DEPENDFILE}) ${OBJS}: ${SRCS:M*.h} .endif .include .include "kern.mk" Index: projects/release-pkg/sys/conf =================================================================== --- projects/release-pkg/sys/conf (revision 295422) +++ projects/release-pkg/sys/conf (revision 295423) Property changes on: projects/release-pkg/sys/conf ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/conf:r295394-295422 Index: projects/release-pkg/sys/kern/init_main.c =================================================================== --- projects/release-pkg/sys/kern/init_main.c (revision 295422) +++ projects/release-pkg/sys/kern/init_main.c (revision 295423) @@ -1,883 +1,883 @@ /*- * Copyright (c) 1995 Terrence R. Lambert * All rights reserved. * * Copyright (c) 1982, 1986, 1989, 1991, 1992, 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. * 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. * * @(#)init_main.c 8.9 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_init_path.h" #include "opt_verbose_sysinit.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 void mi_startup(void); /* Should be elsewhere */ /* Components of the first process -- never freed. */ static struct session session0; static struct pgrp pgrp0; struct proc proc0; struct thread thread0 __aligned(16); struct vmspace vmspace0; struct proc *initproc; #ifndef BOOTHOWTO #define BOOTHOWTO 0 #endif int boothowto = BOOTHOWTO; /* initialized so that it can be patched */ SYSCTL_INT(_debug, OID_AUTO, boothowto, CTLFLAG_RD, &boothowto, 0, "Boot control flags, passed from loader"); #ifndef BOOTVERBOSE #define BOOTVERBOSE 0 #endif int bootverbose = BOOTVERBOSE; SYSCTL_INT(_debug, OID_AUTO, bootverbose, CTLFLAG_RW, &bootverbose, 0, "Control the output of verbose kernel messages"); #ifdef INVARIANTS FEATURE(invariants, "Kernel compiled with INVARIANTS, may affect performance"); #endif /* * This ensures that there is at least one entry so that the sysinit_set * symbol is not undefined. A sybsystem ID of SI_SUB_DUMMY is never * executed. */ SYSINIT(placeholder, SI_SUB_DUMMY, SI_ORDER_ANY, NULL, NULL); /* * The sysinit table itself. Items are checked off as the are run. * If we want to register new sysinit types, add them to newsysinit. */ SET_DECLARE(sysinit_set, struct sysinit); struct sysinit **sysinit, **sysinit_end; struct sysinit **newsysinit, **newsysinit_end; /* * Merge a new sysinit set into the current set, reallocating it if * necessary. This can only be called after malloc is running. */ void sysinit_add(struct sysinit **set, struct sysinit **set_end) { struct sysinit **newset; struct sysinit **sipp; struct sysinit **xipp; int count; count = set_end - set; if (newsysinit) count += newsysinit_end - newsysinit; else count += sysinit_end - sysinit; newset = malloc(count * sizeof(*sipp), M_TEMP, M_NOWAIT); if (newset == NULL) panic("cannot malloc for sysinit"); xipp = newset; if (newsysinit) for (sipp = newsysinit; sipp < newsysinit_end; sipp++) *xipp++ = *sipp; else for (sipp = sysinit; sipp < sysinit_end; sipp++) *xipp++ = *sipp; for (sipp = set; sipp < set_end; sipp++) *xipp++ = *sipp; if (newsysinit) free(newsysinit, M_TEMP); newsysinit = newset; newsysinit_end = newset + count; } #if defined (DDB) && defined(VERBOSE_SYSINIT) static const char * symbol_name(vm_offset_t va, db_strategy_t strategy) { const char *name; c_db_sym_t sym; db_expr_t offset; if (va == 0) return (NULL); sym = db_search_symbol(va, strategy, &offset); if (offset != 0) return (NULL); db_symbol_values(sym, &name, NULL); return (name); } #endif /* * System startup; initialize the world, create process 0, mount root * filesystem, and fork to create init and pagedaemon. Most of the * hard work is done in the lower-level initialization routines including * startup(), which does memory initialization and autoconfiguration. * * This allows simple addition of new kernel subsystems that require * boot time initialization. It also allows substitution of subsystem * (for instance, a scheduler, kernel profiler, or VM system) by object * module. Finally, it allows for optional "kernel threads". */ void mi_startup(void) { register struct sysinit **sipp; /* system initialization*/ register struct sysinit **xipp; /* interior loop of sort*/ register struct sysinit *save; /* bubble*/ #if defined(VERBOSE_SYSINIT) int last; int verbose; #endif if (boothowto & RB_VERBOSE) bootverbose++; if (sysinit == NULL) { sysinit = SET_BEGIN(sysinit_set); sysinit_end = SET_LIMIT(sysinit_set); } restart: /* * Perform a bubble sort of the system initialization objects by * their subsystem (primary key) and order (secondary key). */ for (sipp = sysinit; sipp < sysinit_end; sipp++) { for (xipp = sipp + 1; xipp < sysinit_end; xipp++) { if ((*sipp)->subsystem < (*xipp)->subsystem || ((*sipp)->subsystem == (*xipp)->subsystem && (*sipp)->order <= (*xipp)->order)) continue; /* skip*/ save = *sipp; *sipp = *xipp; *xipp = save; } } #if defined(VERBOSE_SYSINIT) last = SI_SUB_COPYRIGHT; verbose = 0; #if !defined(DDB) printf("VERBOSE_SYSINIT: DDB not enabled, symbol lookups disabled.\n"); #endif #endif /* * Traverse the (now) ordered list of system initialization tasks. * Perform each task, and continue on to the next task. */ for (sipp = sysinit; sipp < sysinit_end; sipp++) { if ((*sipp)->subsystem == SI_SUB_DUMMY) continue; /* skip dummy task(s)*/ if ((*sipp)->subsystem == SI_SUB_DONE) continue; #if defined(VERBOSE_SYSINIT) if ((*sipp)->subsystem > last) { verbose = 1; last = (*sipp)->subsystem; printf("subsystem %x\n", last); } if (verbose) { #if defined(DDB) const char *func, *data; func = symbol_name((vm_offset_t)(*sipp)->func, DB_STGY_PROC); data = symbol_name((vm_offset_t)(*sipp)->udata, DB_STGY_ANY); if (func != NULL && data != NULL) printf(" %s(&%s)... ", func, data); else if (func != NULL) printf(" %s(%p)... ", func, (*sipp)->udata); else #endif printf(" %p(%p)... ", (*sipp)->func, (*sipp)->udata); } #endif /* Call function */ (*((*sipp)->func))((*sipp)->udata); #if defined(VERBOSE_SYSINIT) if (verbose) printf("done.\n"); #endif /* Check off the one we're just done */ (*sipp)->subsystem = SI_SUB_DONE; /* Check if we've installed more sysinit items via KLD */ if (newsysinit != NULL) { if (sysinit != SET_BEGIN(sysinit_set)) free(sysinit, M_TEMP); sysinit = newsysinit; sysinit_end = newsysinit_end; newsysinit = NULL; newsysinit_end = NULL; goto restart; } } mtx_assert(&Giant, MA_OWNED | MA_NOTRECURSED); mtx_unlock(&Giant); /* * Now hand over this thread to swapper. */ swapper(); /* NOTREACHED*/ } /* *************************************************************************** **** **** The following SYSINIT's belong elsewhere, but have not yet **** been moved. **** *************************************************************************** */ static void print_caddr_t(void *data) { printf("%s", (char *)data); } static void print_version(void *data __unused) { int len; /* Strip a trailing newline from version. */ len = strlen(version); while (len > 0 && version[len - 1] == '\n') len--; printf("%.*s %s\n", len, version, machine); printf("%s\n", compiler_version); } SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright); SYSINIT(trademark, SI_SUB_COPYRIGHT, SI_ORDER_SECOND, print_caddr_t, trademark); SYSINIT(version, SI_SUB_COPYRIGHT, SI_ORDER_THIRD, print_version, NULL); #ifdef WITNESS static char wit_warn[] = "WARNING: WITNESS option enabled, expect reduced performance.\n"; SYSINIT(witwarn, SI_SUB_COPYRIGHT, SI_ORDER_THIRD + 1, print_caddr_t, wit_warn); SYSINIT(witwarn2, SI_SUB_LAST, SI_ORDER_THIRD + 1, print_caddr_t, wit_warn); #endif #ifdef DIAGNOSTIC static char diag_warn[] = "WARNING: DIAGNOSTIC option enabled, expect reduced performance.\n"; SYSINIT(diagwarn, SI_SUB_COPYRIGHT, SI_ORDER_THIRD + 2, print_caddr_t, diag_warn); SYSINIT(diagwarn2, SI_SUB_LAST, SI_ORDER_THIRD + 2, print_caddr_t, diag_warn); #endif static int null_fetch_syscall_args(struct thread *td __unused, struct syscall_args *sa __unused) { panic("null_fetch_syscall_args"); } static void null_set_syscall_retval(struct thread *td __unused, int error __unused) { panic("null_set_syscall_retval"); } struct sysentvec null_sysvec = { .sv_size = 0, .sv_table = NULL, .sv_mask = 0, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = NULL, .sv_sendsig = NULL, .sv_sigcode = NULL, .sv_szsigcode = NULL, .sv_name = "null", .sv_coredump = NULL, .sv_imgact_try = NULL, .sv_minsigstksz = 0, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = NULL, .sv_setregs = NULL, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = 0, .sv_set_syscall_retval = null_set_syscall_retval, .sv_fetch_syscall_args = null_fetch_syscall_args, .sv_syscallnames = NULL, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, }; /* *************************************************************************** **** **** The two following SYSINIT's are proc0 specific glue code. I am not **** convinced that they can not be safely combined, but their order of **** operation has been maintained as the same as the original init_main.c **** for right now. **** **** These probably belong in init_proc.c or kern_proc.c, since they **** deal with proc0 (the fork template process). **** *************************************************************************** */ /* ARGSUSED*/ static void proc0_init(void *dummy __unused) { struct proc *p; struct thread *td; struct ucred *newcred; vm_paddr_t pageablemem; int i; GIANT_REQUIRED; p = &proc0; td = &thread0; /* * Initialize magic number and osrel. */ p->p_magic = P_MAGIC; p->p_osrel = osreldate; /* * Initialize thread and process structures. */ procinit(); /* set up proc zone */ threadinit(); /* set up UMA zones */ /* * Initialise scheduler resources. * Add scheduler specific parts to proc, thread as needed. */ schedinit(); /* scheduler gets its house in order */ /* * Create process 0 (the swapper). */ LIST_INSERT_HEAD(&allproc, p, p_list); LIST_INSERT_HEAD(PIDHASH(0), p, p_hash); mtx_init(&pgrp0.pg_mtx, "process group", NULL, MTX_DEF | MTX_DUPOK); p->p_pgrp = &pgrp0; LIST_INSERT_HEAD(PGRPHASH(0), &pgrp0, pg_hash); LIST_INIT(&pgrp0.pg_members); LIST_INSERT_HEAD(&pgrp0.pg_members, p, p_pglist); pgrp0.pg_session = &session0; mtx_init(&session0.s_mtx, "session", NULL, MTX_DEF); refcount_init(&session0.s_count, 1); session0.s_leader = p; p->p_sysent = &null_sysvec; - p->p_flag = P_SYSTEM | P_INMEM; + p->p_flag = P_SYSTEM | P_INMEM | P_KTHREAD; p->p_flag2 = 0; p->p_state = PRS_NORMAL; knlist_init_mtx(&p->p_klist, &p->p_mtx); STAILQ_INIT(&p->p_ktr); p->p_nice = NZERO; /* pid_max cannot be greater than PID_MAX */ td->td_tid = PID_MAX + 1; LIST_INSERT_HEAD(TIDHASH(td->td_tid), td, td_hash); td->td_state = TDS_RUNNING; td->td_pri_class = PRI_TIMESHARE; td->td_user_pri = PUSER; td->td_base_user_pri = PUSER; td->td_lend_user_pri = PRI_MAX; td->td_priority = PVM; td->td_base_pri = PVM; td->td_oncpu = 0; td->td_flags = TDF_INMEM; td->td_pflags = TDP_KTHREAD; td->td_cpuset = cpuset_thread0(); vm_domain_policy_init(&td->td_vm_dom_policy); vm_domain_policy_set(&td->td_vm_dom_policy, VM_POLICY_NONE, -1); vm_domain_policy_init(&p->p_vm_dom_policy); vm_domain_policy_set(&p->p_vm_dom_policy, VM_POLICY_NONE, -1); prison0_init(); p->p_peers = 0; p->p_leader = p; p->p_reaper = p; LIST_INIT(&p->p_reaplist); strncpy(p->p_comm, "kernel", sizeof (p->p_comm)); strncpy(td->td_name, "swapper", sizeof (td->td_name)); callout_init_mtx(&p->p_itcallout, &p->p_mtx, 0); callout_init_mtx(&p->p_limco, &p->p_mtx, 0); callout_init(&td->td_slpcallout, 1); /* Create credentials. */ newcred = crget(); newcred->cr_ngroups = 1; /* group 0 */ newcred->cr_uidinfo = uifind(0); newcred->cr_ruidinfo = uifind(0); newcred->cr_prison = &prison0; newcred->cr_loginclass = loginclass_find("default"); proc_set_cred_init(p, newcred); #ifdef AUDIT audit_cred_kproc0(newcred); #endif #ifdef MAC mac_cred_create_swapper(newcred); #endif /* Create sigacts. */ p->p_sigacts = sigacts_alloc(); /* Initialize signal state for process 0. */ siginit(&proc0); /* Create the file descriptor table. */ p->p_fd = fdinit(NULL, false); p->p_fdtol = NULL; /* Create the limits structures. */ p->p_limit = lim_alloc(); for (i = 0; i < RLIM_NLIMITS; i++) p->p_limit->pl_rlimit[i].rlim_cur = p->p_limit->pl_rlimit[i].rlim_max = RLIM_INFINITY; p->p_limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur = p->p_limit->pl_rlimit[RLIMIT_NOFILE].rlim_max = maxfiles; p->p_limit->pl_rlimit[RLIMIT_NPROC].rlim_cur = p->p_limit->pl_rlimit[RLIMIT_NPROC].rlim_max = maxproc; p->p_limit->pl_rlimit[RLIMIT_DATA].rlim_cur = dfldsiz; p->p_limit->pl_rlimit[RLIMIT_DATA].rlim_max = maxdsiz; p->p_limit->pl_rlimit[RLIMIT_STACK].rlim_cur = dflssiz; p->p_limit->pl_rlimit[RLIMIT_STACK].rlim_max = maxssiz; /* Cast to avoid overflow on i386/PAE. */ pageablemem = ptoa((vm_paddr_t)vm_cnt.v_free_count); p->p_limit->pl_rlimit[RLIMIT_RSS].rlim_cur = p->p_limit->pl_rlimit[RLIMIT_RSS].rlim_max = pageablemem; p->p_limit->pl_rlimit[RLIMIT_MEMLOCK].rlim_cur = pageablemem / 3; p->p_limit->pl_rlimit[RLIMIT_MEMLOCK].rlim_max = pageablemem; p->p_cpulimit = RLIM_INFINITY; PROC_LOCK(p); thread_cow_get_proc(td, p); PROC_UNLOCK(p); /* Initialize resource accounting structures. */ racct_create(&p->p_racct); p->p_stats = pstats_alloc(); /* Allocate a prototype map so we have something to fork. */ p->p_vmspace = &vmspace0; vmspace0.vm_refcnt = 1; pmap_pinit0(vmspace_pmap(&vmspace0)); /* * proc0 is not expected to enter usermode, so there is no special * handling for sv_minuser here, like is done for exec_new_vmspace(). */ vm_map_init(&vmspace0.vm_map, vmspace_pmap(&vmspace0), p->p_sysent->sv_minuser, p->p_sysent->sv_maxuser); /* * Call the init and ctor for the new thread and proc. We wait * to do this until all other structures are fairly sane. */ EVENTHANDLER_INVOKE(process_init, p); EVENTHANDLER_INVOKE(thread_init, td); EVENTHANDLER_INVOKE(process_ctor, p); EVENTHANDLER_INVOKE(thread_ctor, td); /* * Charge root for one process. */ (void)chgproccnt(p->p_ucred->cr_ruidinfo, 1, 0); PROC_LOCK(p); racct_add_force(p, RACCT_NPROC, 1); PROC_UNLOCK(p); } SYSINIT(p0init, SI_SUB_INTRINSIC, SI_ORDER_FIRST, proc0_init, NULL); /* ARGSUSED*/ static void proc0_post(void *dummy __unused) { struct timespec ts; struct proc *p; struct rusage ru; struct thread *td; /* * Now we can look at the time, having had a chance to verify the * time from the filesystem. Pretend that proc0 started now. */ sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { microuptime(&p->p_stats->p_start); PROC_STATLOCK(p); rufetch(p, &ru); /* Clears thread stats */ PROC_STATUNLOCK(p); p->p_rux.rux_runtime = 0; p->p_rux.rux_uticks = 0; p->p_rux.rux_sticks = 0; p->p_rux.rux_iticks = 0; FOREACH_THREAD_IN_PROC(p, td) { td->td_runtime = 0; } } sx_sunlock(&allproc_lock); PCPU_SET(switchtime, cpu_ticks()); PCPU_SET(switchticks, ticks); /* * Give the ``random'' number generator a thump. */ nanotime(&ts); srandom(ts.tv_sec ^ ts.tv_nsec); } SYSINIT(p0post, SI_SUB_INTRINSIC_POST, SI_ORDER_FIRST, proc0_post, NULL); static void random_init(void *dummy __unused) { /* * After CPU has been started we have some randomness on most * platforms via get_cyclecount(). For platforms that don't * we will reseed random(9) in proc0_post() as well. */ srandom(get_cyclecount()); } SYSINIT(random, SI_SUB_RANDOM, SI_ORDER_FIRST, random_init, NULL); /* *************************************************************************** **** **** The following SYSINIT's and glue code should be moved to the **** respective files on a per subsystem basis. **** *************************************************************************** */ /* *************************************************************************** **** **** The following code probably belongs in another file, like **** kern/init_init.c. **** *************************************************************************** */ /* * List of paths to try when searching for "init". */ static char init_path[MAXPATHLEN] = #ifdef INIT_PATH __XSTRING(INIT_PATH); #else "/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init"; #endif SYSCTL_STRING(_kern, OID_AUTO, init_path, CTLFLAG_RD, init_path, 0, "Path used to search the init process"); /* * Shutdown timeout of init(8). * Unused within kernel, but used to control init(8), hence do not remove. */ #ifndef INIT_SHUTDOWN_TIMEOUT #define INIT_SHUTDOWN_TIMEOUT 120 #endif static int init_shutdown_timeout = INIT_SHUTDOWN_TIMEOUT; SYSCTL_INT(_kern, OID_AUTO, init_shutdown_timeout, CTLFLAG_RW, &init_shutdown_timeout, 0, "Shutdown timeout of init(8). " "Unused within kernel, but used to control init(8)"); /* * Start the initial user process; try exec'ing each pathname in init_path. * The program is invoked with one argument containing the boot flags. */ static void start_init(void *dummy) { vm_offset_t addr; struct execve_args args; int options, error; char *var, *path, *next, *s; char *ucp, **uap, *arg0, *arg1; struct thread *td; struct proc *p; mtx_lock(&Giant); GIANT_REQUIRED; td = curthread; p = td->td_proc; vfs_mountroot(); /* Wipe GELI passphrase from the environment. */ kern_unsetenv("kern.geom.eli.passphrase"); /* * Need just enough stack to hold the faked-up "execve()" arguments. */ addr = p->p_sysent->sv_usrstack - PAGE_SIZE; if (vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &addr, PAGE_SIZE, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0) != 0) panic("init: couldn't allocate argument space"); p->p_vmspace->vm_maxsaddr = (caddr_t)addr; p->p_vmspace->vm_ssize = 1; if ((var = kern_getenv("init_path")) != NULL) { strlcpy(init_path, var, sizeof(init_path)); freeenv(var); } for (path = init_path; *path != '\0'; path = next) { while (*path == ':') path++; if (*path == '\0') break; for (next = path; *next != '\0' && *next != ':'; next++) /* nothing */ ; if (bootverbose) printf("start_init: trying %.*s\n", (int)(next - path), path); /* * Move out the boot flag argument. */ options = 0; ucp = (char *)p->p_sysent->sv_usrstack; (void)subyte(--ucp, 0); /* trailing zero */ if (boothowto & RB_SINGLE) { (void)subyte(--ucp, 's'); options = 1; } #ifdef notyet if (boothowto & RB_FASTBOOT) { (void)subyte(--ucp, 'f'); options = 1; } #endif #ifdef BOOTCDROM (void)subyte(--ucp, 'C'); options = 1; #endif if (options == 0) (void)subyte(--ucp, '-'); (void)subyte(--ucp, '-'); /* leading hyphen */ arg1 = ucp; /* * Move out the file name (also arg 0). */ (void)subyte(--ucp, 0); for (s = next - 1; s >= path; s--) (void)subyte(--ucp, *s); arg0 = ucp; /* * Move out the arg pointers. */ uap = (char **)((intptr_t)ucp & ~(sizeof(intptr_t)-1)); (void)suword((caddr_t)--uap, (long)0); /* terminator */ (void)suword((caddr_t)--uap, (long)(intptr_t)arg1); (void)suword((caddr_t)--uap, (long)(intptr_t)arg0); /* * Point at the arguments. */ args.fname = arg0; args.argv = uap; args.envv = NULL; /* * Now try to exec the program. If can't for any reason * other than it doesn't exist, complain. * * Otherwise, return via fork_trampoline() all the way * to user mode as init! */ if ((error = sys_execve(td, &args)) == 0) { mtx_unlock(&Giant); return; } if (error != ENOENT) printf("exec %.*s: error %d\n", (int)(next - path), path, error); } printf("init: not found in path %s\n", init_path); panic("no init"); } /* * Like kproc_create(), but runs in it's own address space. * We do this early to reserve pid 1. * * Note special case - do not make it runnable yet. Other work * in progress will change this more. */ static void create_init(const void *udata __unused) { struct fork_req fr; struct ucred *newcred, *oldcred; struct thread *td; int error; bzero(&fr, sizeof(fr)); fr.fr_flags = RFFDG | RFPROC | RFSTOPPED; fr.fr_procp = &initproc; error = fork1(&thread0, &fr); if (error) panic("cannot fork init: %d\n", error); KASSERT(initproc->p_pid == 1, ("create_init: initproc->p_pid != 1")); /* divorce init's credentials from the kernel's */ newcred = crget(); sx_xlock(&proctree_lock); PROC_LOCK(initproc); initproc->p_flag |= P_SYSTEM | P_INMEM; initproc->p_treeflag |= P_TREE_REAPER; LIST_INSERT_HEAD(&initproc->p_reaplist, &proc0, p_reapsibling); oldcred = initproc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC mac_cred_create_init(newcred); #endif #ifdef AUDIT audit_cred_proc1(newcred); #endif proc_set_cred(initproc, newcred); td = FIRST_THREAD_IN_PROC(initproc); crfree(td->td_ucred); td->td_ucred = crhold(initproc->p_ucred); PROC_UNLOCK(initproc); sx_xunlock(&proctree_lock); crfree(oldcred); cpu_set_fork_handler(FIRST_THREAD_IN_PROC(initproc), start_init, NULL); } SYSINIT(init, SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL); /* * Make it runnable now. */ static void kick_init(const void *udata __unused) { struct thread *td; td = FIRST_THREAD_IN_PROC(initproc); thread_lock(td); TD_SET_CAN_RUN(td); sched_add(td, SRQ_BORING); thread_unlock(td); } SYSINIT(kickinit, SI_SUB_KTHREAD_INIT, SI_ORDER_MIDDLE, kick_init, NULL); Index: projects/release-pkg/sys/kern/kern_fork.c =================================================================== --- projects/release-pkg/sys/kern/kern_fork.c (revision 295422) +++ projects/release-pkg/sys/kern/kern_fork.c (revision 295423) @@ -1,1114 +1,1114 @@ /*- * 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_fork.c 8.6 (Berkeley) 4/8/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_ktrace.h" #include "opt_kstack_pages.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 #ifdef KDTRACE_HOOKS #include dtrace_fork_func_t dtrace_fasttrap_fork; #endif SDT_PROVIDER_DECLARE(proc); SDT_PROBE_DEFINE3(proc, , , create, "struct proc *", "struct proc *", "int"); #ifndef _SYS_SYSPROTO_H_ struct fork_args { int dummy; }; #endif /* ARGSUSED */ int sys_fork(struct thread *td, struct fork_args *uap) { struct fork_req fr; int error, pid; bzero(&fr, sizeof(fr)); fr.fr_flags = RFFDG | RFPROC; fr.fr_pidp = &pid; error = fork1(td, &fr); if (error == 0) { td->td_retval[0] = pid; td->td_retval[1] = 0; } return (error); } /* ARGUSED */ int sys_pdfork(struct thread *td, struct pdfork_args *uap) { struct fork_req fr; int error, fd, pid; bzero(&fr, sizeof(fr)); fr.fr_flags = RFFDG | RFPROC | RFPROCDESC; fr.fr_pidp = &pid; fr.fr_pd_fd = &fd; fr.fr_pd_flags = uap->flags; /* * It is necessary to return fd by reference because 0 is a valid file * descriptor number, and the child needs to be able to distinguish * itself from the parent using the return value. */ error = fork1(td, &fr); if (error == 0) { td->td_retval[0] = pid; td->td_retval[1] = 0; error = copyout(&fd, uap->fdp, sizeof(fd)); } return (error); } /* ARGSUSED */ int sys_vfork(struct thread *td, struct vfork_args *uap) { struct fork_req fr; int error, pid; bzero(&fr, sizeof(fr)); fr.fr_flags = RFFDG | RFPROC | RFPPWAIT | RFMEM; fr.fr_pidp = &pid; error = fork1(td, &fr); if (error == 0) { td->td_retval[0] = pid; td->td_retval[1] = 0; } return (error); } int sys_rfork(struct thread *td, struct rfork_args *uap) { struct fork_req fr; int error, pid; /* Don't allow kernel-only flags. */ if ((uap->flags & RFKERNELONLY) != 0) return (EINVAL); AUDIT_ARG_FFLAGS(uap->flags); bzero(&fr, sizeof(fr)); fr.fr_flags = uap->flags; fr.fr_pidp = &pid; error = fork1(td, &fr); if (error == 0) { td->td_retval[0] = pid; td->td_retval[1] = 0; } return (error); } int nprocs = 1; /* process 0 */ int lastpid = 0; SYSCTL_INT(_kern, OID_AUTO, lastpid, CTLFLAG_RD, &lastpid, 0, "Last used PID"); /* * Random component to lastpid generation. We mix in a random factor to make * it a little harder to predict. We sanity check the modulus value to avoid * doing it in critical paths. Don't let it be too small or we pointlessly * waste randomness entropy, and don't let it be impossibly large. Using a * modulus that is too big causes a LOT more process table scans and slows * down fork processing as the pidchecked caching is defeated. */ static int randompid = 0; static int sysctl_kern_randompid(SYSCTL_HANDLER_ARGS) { int error, pid; error = sysctl_wire_old_buffer(req, sizeof(int)); if (error != 0) return(error); sx_xlock(&allproc_lock); pid = randompid; error = sysctl_handle_int(oidp, &pid, 0, req); if (error == 0 && req->newptr != NULL) { if (pid < 0 || pid > pid_max - 100) /* out of range */ pid = pid_max - 100; else if (pid < 2) /* NOP */ pid = 0; else if (pid < 100) /* Make it reasonable */ pid = 100; randompid = pid; } sx_xunlock(&allproc_lock); return (error); } SYSCTL_PROC(_kern, OID_AUTO, randompid, CTLTYPE_INT|CTLFLAG_RW, 0, 0, sysctl_kern_randompid, "I", "Random PID modulus"); static int fork_findpid(int flags) { struct proc *p; int trypid; static int pidchecked = 0; /* * Requires allproc_lock in order to iterate over the list * of processes, and proctree_lock to access p_pgrp. */ sx_assert(&allproc_lock, SX_LOCKED); sx_assert(&proctree_lock, SX_LOCKED); /* * Find an unused process ID. We remember a range of unused IDs * ready to use (from lastpid+1 through pidchecked-1). * * If RFHIGHPID is set (used during system boot), do not allocate * low-numbered pids. */ trypid = lastpid + 1; if (flags & RFHIGHPID) { if (trypid < 10) trypid = 10; } else { if (randompid) trypid += arc4random() % randompid; } retry: /* * If the process ID prototype has wrapped around, * restart somewhat above 0, as the low-numbered procs * tend to include daemons that don't exit. */ if (trypid >= pid_max) { trypid = trypid % pid_max; if (trypid < 100) trypid += 100; pidchecked = 0; } if (trypid >= pidchecked) { int doingzomb = 0; pidchecked = PID_MAX; /* * Scan the active and zombie procs to check whether this pid * is in use. Remember the lowest pid that's greater * than trypid, so we can avoid checking for a while. * * Avoid reuse of the process group id, session id or * the reaper subtree id. Note that for process group * and sessions, the amount of reserved pids is * limited by process limit. For the subtree ids, the * id is kept reserved only while there is a * non-reaped process in the subtree, so amount of * reserved pids is limited by process limit times * two. */ p = LIST_FIRST(&allproc); again: for (; p != NULL; p = LIST_NEXT(p, p_list)) { while (p->p_pid == trypid || p->p_reapsubtree == trypid || (p->p_pgrp != NULL && (p->p_pgrp->pg_id == trypid || (p->p_session != NULL && p->p_session->s_sid == trypid)))) { trypid++; if (trypid >= pidchecked) goto retry; } if (p->p_pid > trypid && pidchecked > p->p_pid) pidchecked = p->p_pid; if (p->p_pgrp != NULL) { if (p->p_pgrp->pg_id > trypid && pidchecked > p->p_pgrp->pg_id) pidchecked = p->p_pgrp->pg_id; if (p->p_session != NULL && p->p_session->s_sid > trypid && pidchecked > p->p_session->s_sid) pidchecked = p->p_session->s_sid; } } if (!doingzomb) { doingzomb = 1; p = LIST_FIRST(&zombproc); goto again; } } /* * RFHIGHPID does not mess with the lastpid counter during boot. */ if (flags & RFHIGHPID) pidchecked = 0; else lastpid = trypid; return (trypid); } static int fork_norfproc(struct thread *td, int flags) { int error; struct proc *p1; KASSERT((flags & RFPROC) == 0, ("fork_norfproc called with RFPROC set")); p1 = td->td_proc; if (((p1->p_flag & (P_HADTHREADS|P_SYSTEM)) == P_HADTHREADS) && (flags & (RFCFDG | RFFDG))) { PROC_LOCK(p1); if (thread_single(p1, SINGLE_BOUNDARY)) { PROC_UNLOCK(p1); return (ERESTART); } PROC_UNLOCK(p1); } error = vm_forkproc(td, NULL, NULL, NULL, flags); if (error) goto fail; /* * Close all file descriptors. */ if (flags & RFCFDG) { struct filedesc *fdtmp; fdtmp = fdinit(td->td_proc->p_fd, false); fdescfree(td); p1->p_fd = fdtmp; } /* * Unshare file descriptors (from parent). */ if (flags & RFFDG) fdunshare(td); fail: if (((p1->p_flag & (P_HADTHREADS|P_SYSTEM)) == P_HADTHREADS) && (flags & (RFCFDG | RFFDG))) { PROC_LOCK(p1); thread_single_end(p1, SINGLE_BOUNDARY); PROC_UNLOCK(p1); } return (error); } static void do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *td2, struct vmspace *vm2, struct file *fp_procdesc) { struct proc *p1, *pptr; int trypid; struct filedesc *fd; struct filedesc_to_leader *fdtol; struct sigacts *newsigacts; sx_assert(&proctree_lock, SX_SLOCKED); sx_assert(&allproc_lock, SX_XLOCKED); p1 = td->td_proc; trypid = fork_findpid(fr->fr_flags); sx_sunlock(&proctree_lock); p2->p_state = PRS_NEW; /* protect against others */ p2->p_pid = trypid; AUDIT_ARG_PID(p2->p_pid); LIST_INSERT_HEAD(&allproc, p2, p_list); allproc_gen++; LIST_INSERT_HEAD(PIDHASH(p2->p_pid), p2, p_hash); tidhash_add(td2); PROC_LOCK(p2); PROC_LOCK(p1); sx_xunlock(&allproc_lock); bcopy(&p1->p_startcopy, &p2->p_startcopy, __rangeof(struct proc, p_startcopy, p_endcopy)); pargs_hold(p2->p_args); PROC_UNLOCK(p1); bzero(&p2->p_startzero, __rangeof(struct proc, p_startzero, p_endzero)); /* Tell the prison that we exist. */ prison_proc_hold(p2->p_ucred->cr_prison); PROC_UNLOCK(p2); /* * Malloc things while we don't hold any locks. */ if (fr->fr_flags & RFSIGSHARE) newsigacts = NULL; else newsigacts = sigacts_alloc(); /* * Copy filedesc. */ if (fr->fr_flags & RFCFDG) { fd = fdinit(p1->p_fd, false); fdtol = NULL; } else if (fr->fr_flags & RFFDG) { fd = fdcopy(p1->p_fd); fdtol = NULL; } else { fd = fdshare(p1->p_fd); if (p1->p_fdtol == NULL) p1->p_fdtol = filedesc_to_leader_alloc(NULL, NULL, p1->p_leader); if ((fr->fr_flags & RFTHREAD) != 0) { /* * Shared file descriptor table, and shared * process leaders. */ fdtol = p1->p_fdtol; FILEDESC_XLOCK(p1->p_fd); fdtol->fdl_refcount++; FILEDESC_XUNLOCK(p1->p_fd); } else { /* * Shared file descriptor table, and different * process leaders. */ fdtol = filedesc_to_leader_alloc(p1->p_fdtol, p1->p_fd, p2); } } /* * Make a proc table entry for the new process. * Start by zeroing the section of proc that is zero-initialized, * then copy the section that is copied directly from the parent. */ PROC_LOCK(p2); PROC_LOCK(p1); bzero(&td2->td_startzero, __rangeof(struct thread, td_startzero, td_endzero)); bcopy(&td->td_startcopy, &td2->td_startcopy, __rangeof(struct thread, td_startcopy, td_endcopy)); bcopy(&p2->p_comm, &td2->td_name, sizeof(td2->td_name)); td2->td_sigstk = td->td_sigstk; td2->td_flags = TDF_INMEM; td2->td_lend_user_pri = PRI_MAX; #ifdef VIMAGE td2->td_vnet = NULL; td2->td_vnet_lpush = NULL; #endif /* * Allow the scheduler to initialize the child. */ thread_lock(td); sched_fork(td, td2); thread_unlock(td); /* * Duplicate sub-structures as needed. * Increase reference counts on shared objects. */ p2->p_flag = P_INMEM; p2->p_flag2 = p1->p_flag2 & (P2_NOTRACE | P2_NOTRACE_EXEC); p2->p_swtick = ticks; if (p1->p_flag & P_PROFIL) startprofclock(p2); /* * Whilst the proc lock is held, copy the VM domain data out * using the VM domain method. */ vm_domain_policy_init(&p2->p_vm_dom_policy); vm_domain_policy_localcopy(&p2->p_vm_dom_policy, &p1->p_vm_dom_policy); if (fr->fr_flags & RFSIGSHARE) { p2->p_sigacts = sigacts_hold(p1->p_sigacts); } else { sigacts_copy(newsigacts, p1->p_sigacts); p2->p_sigacts = newsigacts; } if (fr->fr_flags & RFTSIGZMB) p2->p_sigparent = RFTSIGNUM(fr->fr_flags); else if (fr->fr_flags & RFLINUXTHPN) p2->p_sigparent = SIGUSR1; else p2->p_sigparent = SIGCHLD; p2->p_textvp = p1->p_textvp; p2->p_fd = fd; p2->p_fdtol = fdtol; if (p1->p_flag2 & P2_INHERIT_PROTECTED) { p2->p_flag |= P_PROTECTED; p2->p_flag2 |= P2_INHERIT_PROTECTED; } /* * p_limit is copy-on-write. Bump its refcount. */ lim_fork(p1, p2); thread_cow_get_proc(td2, p2); pstats_fork(p1->p_stats, p2->p_stats); PROC_UNLOCK(p1); PROC_UNLOCK(p2); /* Bump references to the text vnode (for procfs). */ if (p2->p_textvp) vref(p2->p_textvp); /* * Set up linkage for kernel based threading. */ if ((fr->fr_flags & RFTHREAD) != 0) { mtx_lock(&ppeers_lock); p2->p_peers = p1->p_peers; p1->p_peers = p2; p2->p_leader = p1->p_leader; mtx_unlock(&ppeers_lock); PROC_LOCK(p1->p_leader); if ((p1->p_leader->p_flag & P_WEXIT) != 0) { PROC_UNLOCK(p1->p_leader); /* * The task leader is exiting, so process p1 is * going to be killed shortly. Since p1 obviously * isn't dead yet, we know that the leader is either * sending SIGKILL's to all the processes in this * task or is sleeping waiting for all the peers to * exit. We let p1 complete the fork, but we need * to go ahead and kill the new process p2 since * the task leader may not get a chance to send * SIGKILL to it. We leave it on the list so that * the task leader will wait for this new process * to commit suicide. */ PROC_LOCK(p2); kern_psignal(p2, SIGKILL); PROC_UNLOCK(p2); } else PROC_UNLOCK(p1->p_leader); } else { p2->p_peers = NULL; p2->p_leader = p2; } sx_xlock(&proctree_lock); PGRP_LOCK(p1->p_pgrp); PROC_LOCK(p2); PROC_LOCK(p1); /* * Preserve some more flags in subprocess. P_PROFIL has already * been preserved. */ p2->p_flag |= p1->p_flag & P_SUGID; td2->td_pflags |= (td->td_pflags & TDP_ALTSTACK) | TDP_FORKING; SESS_LOCK(p1->p_session); if (p1->p_session->s_ttyvp != NULL && p1->p_flag & P_CONTROLT) p2->p_flag |= P_CONTROLT; SESS_UNLOCK(p1->p_session); if (fr->fr_flags & RFPPWAIT) p2->p_flag |= P_PPWAIT; p2->p_pgrp = p1->p_pgrp; LIST_INSERT_AFTER(p1, p2, p_pglist); PGRP_UNLOCK(p1->p_pgrp); LIST_INIT(&p2->p_children); LIST_INIT(&p2->p_orphans); callout_init_mtx(&p2->p_itcallout, &p2->p_mtx, 0); /* * If PF_FORK is set, the child process inherits the * procfs ioctl flags from its parent. */ if (p1->p_pfsflags & PF_FORK) { p2->p_stops = p1->p_stops; p2->p_pfsflags = p1->p_pfsflags; } /* * This begins the section where we must prevent the parent * from being swapped. */ _PHOLD(p1); PROC_UNLOCK(p1); /* * Attach the new process to its parent. * * If RFNOWAIT is set, the newly created process becomes a child * of init. This effectively disassociates the child from the * parent. */ if ((fr->fr_flags & RFNOWAIT) != 0) { pptr = p1->p_reaper; p2->p_reaper = pptr; } else { p2->p_reaper = (p1->p_treeflag & P_TREE_REAPER) != 0 ? p1 : p1->p_reaper; pptr = p1; } p2->p_pptr = pptr; LIST_INSERT_HEAD(&pptr->p_children, p2, p_sibling); LIST_INIT(&p2->p_reaplist); LIST_INSERT_HEAD(&p2->p_reaper->p_reaplist, p2, p_reapsibling); if (p2->p_reaper == p1) p2->p_reapsubtree = p2->p_pid; sx_xunlock(&proctree_lock); /* Inform accounting that we have forked. */ p2->p_acflag = AFORK; PROC_UNLOCK(p2); #ifdef KTRACE ktrprocfork(p1, p2); #endif /* * Finish creating the child process. It will return via a different * execution path later. (ie: directly into user mode) */ vm_forkproc(td, p2, td2, vm2, fr->fr_flags); if (fr->fr_flags == (RFFDG | RFPROC)) { PCPU_INC(cnt.v_forks); PCPU_ADD(cnt.v_forkpages, p2->p_vmspace->vm_dsize + p2->p_vmspace->vm_ssize); } else if (fr->fr_flags == (RFFDG | RFPROC | RFPPWAIT | RFMEM)) { PCPU_INC(cnt.v_vforks); PCPU_ADD(cnt.v_vforkpages, p2->p_vmspace->vm_dsize + p2->p_vmspace->vm_ssize); } else if (p1 == &proc0) { PCPU_INC(cnt.v_kthreads); PCPU_ADD(cnt.v_kthreadpages, p2->p_vmspace->vm_dsize + p2->p_vmspace->vm_ssize); } else { PCPU_INC(cnt.v_rforks); PCPU_ADD(cnt.v_rforkpages, p2->p_vmspace->vm_dsize + p2->p_vmspace->vm_ssize); } /* * Associate the process descriptor with the process before anything * can happen that might cause that process to need the descriptor. * However, don't do this until after fork(2) can no longer fail. */ if (fr->fr_flags & RFPROCDESC) procdesc_new(p2, fr->fr_pd_flags); /* * Both processes are set up, now check if any loadable modules want * to adjust anything. */ EVENTHANDLER_INVOKE(process_fork, p1, p2, fr->fr_flags); /* * Set the child start time and mark the process as being complete. */ PROC_LOCK(p2); PROC_LOCK(p1); microuptime(&p2->p_stats->p_start); PROC_SLOCK(p2); p2->p_state = PRS_NORMAL; PROC_SUNLOCK(p2); #ifdef KDTRACE_HOOKS /* * Tell the DTrace fasttrap provider about the new process so that any * tracepoints inherited from the parent can be removed. We have to do * this only after p_state is PRS_NORMAL since the fasttrap module will * use pfind() later on. */ if ((fr->fr_flags & RFMEM) == 0 && dtrace_fasttrap_fork) dtrace_fasttrap_fork(p1, p2); #endif /* * Hold the process so that it cannot exit after we make it runnable, * but before we wait for the debugger. */ _PHOLD(p2); if ((p1->p_flag & (P_TRACED | P_FOLLOWFORK)) == (P_TRACED | P_FOLLOWFORK)) { /* * Arrange for debugger to receive the fork event. * * We can report PL_FLAG_FORKED regardless of * P_FOLLOWFORK settings, but it does not make a sense * for runaway child. */ td->td_dbgflags |= TDB_FORK; td->td_dbg_forked = p2->p_pid; td2->td_dbgflags |= TDB_STOPATFORK; } if (fr->fr_flags & RFPPWAIT) { td->td_pflags |= TDP_RFPPWAIT; td->td_rfppwait_p = p2; } PROC_UNLOCK(p2); /* * Now can be swapped. */ _PRELE(p1); PROC_UNLOCK(p1); /* * Tell any interested parties about the new process. */ knote_fork(&p1->p_klist, p2->p_pid); SDT_PROBE3(proc, , , create, p2, p1, fr->fr_flags); if (fr->fr_flags & RFPROCDESC) { procdesc_finit(p2->p_procdesc, fp_procdesc); fdrop(fp_procdesc, td); } if ((fr->fr_flags & RFSTOPPED) == 0) { /* * If RFSTOPPED not requested, make child runnable and * add to run queue. */ thread_lock(td2); TD_SET_CAN_RUN(td2); sched_add(td2, SRQ_BORING); thread_unlock(td2); if (fr->fr_pidp != NULL) *fr->fr_pidp = p2->p_pid; } else { *fr->fr_procp = p2; } PROC_LOCK(p2); /* * Wait until debugger is attached to child. */ while (td2->td_proc == p2 && (td2->td_dbgflags & TDB_STOPATFORK) != 0) cv_wait(&p2->p_dbgwait, &p2->p_mtx); _PRELE(p2); racct_proc_fork_done(p2); PROC_UNLOCK(p2); } int fork1(struct thread *td, struct fork_req *fr) { struct proc *p1, *newproc; struct thread *td2; struct vmspace *vm2; struct file *fp_procdesc; vm_ooffset_t mem_charged; int error, nprocs_new, ok; static int curfail; static struct timeval lastfail; int flags, pages; flags = fr->fr_flags; pages = fr->fr_pages; if ((flags & RFSTOPPED) != 0) MPASS(fr->fr_procp != NULL && fr->fr_pidp == NULL); else MPASS(fr->fr_procp == NULL); /* Check for the undefined or unimplemented flags. */ if ((flags & ~(RFFLAGS | RFTSIGFLAGS(RFTSIGMASK))) != 0) return (EINVAL); /* Signal value requires RFTSIGZMB. */ if ((flags & RFTSIGFLAGS(RFTSIGMASK)) != 0 && (flags & RFTSIGZMB) == 0) return (EINVAL); /* Can't copy and clear. */ if ((flags & (RFFDG|RFCFDG)) == (RFFDG|RFCFDG)) return (EINVAL); /* Check the validity of the signal number. */ if ((flags & RFTSIGZMB) != 0 && (u_int)RFTSIGNUM(flags) > _SIG_MAXSIG) return (EINVAL); if ((flags & RFPROCDESC) != 0) { /* Can't not create a process yet get a process descriptor. */ if ((flags & RFPROC) == 0) return (EINVAL); /* Must provide a place to put a procdesc if creating one. */ if (fr->fr_pd_fd == NULL) return (EINVAL); } p1 = td->td_proc; /* * Here we don't create a new process, but we divorce * certain parts of a process from itself. */ if ((flags & RFPROC) == 0) { if (fr->fr_procp != NULL) *fr->fr_procp = NULL; else if (fr->fr_pidp != NULL) *fr->fr_pidp = 0; return (fork_norfproc(td, flags)); } fp_procdesc = NULL; newproc = NULL; vm2 = NULL; /* * Increment the nprocs resource before allocations occur. * Although process entries are dynamically created, we still * keep a global limit on the maximum number we will * create. There are hard-limits as to the number of processes * that can run, established by the KVA and memory usage for * the process data. * * Don't allow a nonprivileged user to use the last ten * processes; don't let root exceed the limit. */ nprocs_new = atomic_fetchadd_int(&nprocs, 1) + 1; if ((nprocs_new >= maxproc - 10 && priv_check_cred(td->td_ucred, PRIV_MAXPROC, 0) != 0) || nprocs_new >= maxproc) { error = EAGAIN; sx_xlock(&allproc_lock); if (ppsratecheck(&lastfail, &curfail, 1)) { printf("maxproc limit exceeded by uid %u (pid %d); " "see tuning(7) and login.conf(5)\n", td->td_ucred->cr_ruid, p1->p_pid); } sx_xunlock(&allproc_lock); goto fail2; } /* * If required, create a process descriptor in the parent first; we * will abandon it if something goes wrong. We don't finit() until * later. */ if (flags & RFPROCDESC) { error = falloc_caps(td, &fp_procdesc, fr->fr_pd_fd, 0, fr->fr_pd_fcaps); if (error != 0) goto fail2; } mem_charged = 0; if (pages == 0) pages = kstack_pages; /* Allocate new proc. */ newproc = uma_zalloc(proc_zone, M_WAITOK); td2 = FIRST_THREAD_IN_PROC(newproc); if (td2 == NULL) { td2 = thread_alloc(pages); if (td2 == NULL) { error = ENOMEM; goto fail2; } proc_linkup(newproc, td2); } else { if (td2->td_kstack == 0 || td2->td_kstack_pages != pages) { if (td2->td_kstack != 0) vm_thread_dispose(td2); if (!thread_alloc_stack(td2, pages)) { error = ENOMEM; goto fail2; } } } if ((flags & RFMEM) == 0) { vm2 = vmspace_fork(p1->p_vmspace, &mem_charged); if (vm2 == NULL) { error = ENOMEM; goto fail2; } if (!swap_reserve(mem_charged)) { /* * The swap reservation failed. The accounting * from the entries of the copied vm2 will be * substracted in vmspace_free(), so force the * reservation there. */ swap_reserve_force(mem_charged); error = ENOMEM; goto fail2; } } else vm2 = NULL; /* * XXX: This is ugly; when we copy resource usage, we need to bump * per-cred resource counters. */ proc_set_cred_init(newproc, crhold(td->td_ucred)); /* * Initialize resource accounting for the child process. */ error = racct_proc_fork(p1, newproc); if (error != 0) { error = EAGAIN; goto fail1; } #ifdef MAC mac_proc_init(newproc); #endif knlist_init_mtx(&newproc->p_klist, &newproc->p_mtx); STAILQ_INIT(&newproc->p_ktr); /* We have to lock the process tree while we look for a pid. */ sx_slock(&proctree_lock); sx_xlock(&allproc_lock); /* * Increment the count of procs running with this uid. Don't allow * a nonprivileged user to exceed their current limit. * * XXXRW: Can we avoid privilege here if it's not needed? */ error = priv_check_cred(td->td_ucred, PRIV_PROC_LIMIT, 0); if (error == 0) ok = chgproccnt(td->td_ucred->cr_ruidinfo, 1, 0); else { ok = chgproccnt(td->td_ucred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_NPROC)); } if (ok) { do_fork(td, fr, newproc, td2, vm2, fp_procdesc); return (0); } error = EAGAIN; sx_sunlock(&proctree_lock); sx_xunlock(&allproc_lock); #ifdef MAC mac_proc_destroy(newproc); #endif racct_proc_exit(newproc); fail1: crfree(newproc->p_ucred); newproc->p_ucred = NULL; fail2: if (vm2 != NULL) vmspace_free(vm2); uma_zfree(proc_zone, newproc); if ((flags & RFPROCDESC) != 0 && fp_procdesc != NULL) { fdclose(td, fp_procdesc, *fr->fr_pd_fd); fdrop(fp_procdesc, td); } atomic_add_int(&nprocs, -1); pause("fork", hz / 2); return (error); } /* * Handle the return of a child process from fork1(). This function * is called from the MD fork_trampoline() entry point. */ void fork_exit(void (*callout)(void *, struct trapframe *), void *arg, struct trapframe *frame) { struct proc *p; struct thread *td; struct thread *dtd; td = curthread; p = td->td_proc; KASSERT(p->p_state == PRS_NORMAL, ("executing process is still new")); CTR4(KTR_PROC, "fork_exit: new thread %p (td_sched %p, pid %d, %s)", td, td->td_sched, p->p_pid, td->td_name); sched_fork_exit(td); /* * Processes normally resume in mi_switch() after being * cpu_switch()'ed to, but when children start up they arrive here * instead, so we must do much the same things as mi_switch() would. */ if ((dtd = PCPU_GET(deadthread))) { PCPU_SET(deadthread, NULL); thread_stash(dtd); } thread_unlock(td); /* * cpu_set_fork_handler intercepts this function call to * have this call a non-return function to stay in kernel mode. * initproc has its own fork handler, but it does return. */ KASSERT(callout != NULL, ("NULL callout in fork_exit")); callout(arg, frame); /* * Check if a kernel thread misbehaved and returned from its main * function. */ if (p->p_flag & P_KTHREAD) { printf("Kernel thread \"%s\" (pid %d) exited prematurely.\n", td->td_name, p->p_pid); - kproc_exit(0); + kthread_exit(); } mtx_assert(&Giant, MA_NOTOWNED); if (p->p_sysent->sv_schedtail != NULL) (p->p_sysent->sv_schedtail)(td); td->td_pflags &= ~TDP_FORKING; } /* * Simplified back end of syscall(), used when returning from fork() * directly into user mode. Giant is not held on entry, and must not * be held on return. This function is passed in to fork_exit() as the * first parameter and is called when returning to a new userland process. */ void fork_return(struct thread *td, struct trapframe *frame) { struct proc *p, *dbg; p = td->td_proc; if (td->td_dbgflags & TDB_STOPATFORK) { sx_xlock(&proctree_lock); PROC_LOCK(p); if ((p->p_pptr->p_flag & (P_TRACED | P_FOLLOWFORK)) == (P_TRACED | P_FOLLOWFORK)) { /* * If debugger still wants auto-attach for the * parent's children, do it now. */ dbg = p->p_pptr->p_pptr; p->p_flag |= P_TRACED; p->p_oppid = p->p_pptr->p_pid; CTR2(KTR_PTRACE, "fork_return: attaching to new child pid %d: oppid %d", p->p_pid, p->p_oppid); proc_reparent(p, dbg); sx_xunlock(&proctree_lock); td->td_dbgflags |= TDB_CHILD | TDB_SCX; ptracestop(td, SIGSTOP); td->td_dbgflags &= ~(TDB_CHILD | TDB_SCX); } else { /* * ... otherwise clear the request. */ sx_xunlock(&proctree_lock); td->td_dbgflags &= ~TDB_STOPATFORK; cv_broadcast(&p->p_dbgwait); } PROC_UNLOCK(p); } else if (p->p_flag & P_TRACED || td->td_dbgflags & TDB_BORN) { /* * This is the start of a new thread in a traced * process. Report a system call exit event. */ PROC_LOCK(p); td->td_dbgflags |= TDB_SCX; _STOPEVENT(p, S_SCX, td->td_dbg_sc_code); if ((p->p_stops & S_PT_SCX) != 0 || (td->td_dbgflags & TDB_BORN) != 0) ptracestop(td, SIGTRAP); td->td_dbgflags &= ~(TDB_SCX | TDB_BORN); PROC_UNLOCK(p); } userret(td, frame); #ifdef KTRACE if (KTRPOINT(td, KTR_SYSRET)) ktrsysret(SYS_fork, 0, 0); #endif } Index: projects/release-pkg/sys =================================================================== --- projects/release-pkg/sys (revision 295422) +++ projects/release-pkg/sys (revision 295423) Property changes on: projects/release-pkg/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r295394-295422 Index: projects/release-pkg/targets/pseudo/userland/misc/Makefile.depend =================================================================== --- projects/release-pkg/targets/pseudo/userland/misc/Makefile.depend (revision 295422) +++ projects/release-pkg/targets/pseudo/userland/misc/Makefile.depend (revision 295423) @@ -1,85 +1,86 @@ # $FreeBSD$ # This file is not autogenerated - take care! .if !defined(MK_FORTH) .include .endif _sys_boot_efi= sys/boot/efi/loader sys/boot/efi/boot1 .if ${MK_FDT} != "no" _sys_boot_fdt= sys/boot/fdt sys/boot/efi/fdt .endif .if ${MK_ZFS} != "no" _sys_boot_zfs= sys/boot/zfs .endif DIRDEPS = \ etc \ etc/newsyslog.conf.d \ etc/sendmail \ rescue/librescue \ .if ${MK_BOOT} != "no" DIRDEPS+= sys/boot/common .if ${MK_FORTH} != "no" DIRDEPS+= \ sys/boot/ficl \ sys/boot/forth \ .endif DIRDEPS.x86sys= \ sys/boot/efi/libefi \ sys/boot/i386/boot0 \ sys/boot/i386/boot0sio \ sys/boot/i386/boot2 \ sys/boot/i386/btx/btx \ sys/boot/i386/btx/btxldr \ sys/boot/i386/btx/lib \ sys/boot/i386/cdboot \ sys/boot/i386/gptboot \ + sys/boot/i386/kgzldr \ sys/boot/i386/libfirewire \ sys/boot/i386/libi386 \ sys/boot/i386/loader \ sys/boot/i386/mbr \ sys/boot/i386/pmbr \ sys/boot/i386/pxeldr \ sys/boot/libstand32 \ ${_sys_boot_zfs} \ .if ${MK_ZFS} != "no" DIRDEPS.x86sys+= \ sys/boot/i386/gptzfsboot \ sys/boot/i386/zfsboot \ sys/boot/i386/zfsloader \ .endif DIRDEPS.amd64= \ ${DIRDEPS.x86sys} \ ${_sys_boot_efi} \ sys/boot/ficl32 \ sys/boot/userboot/ficl \ sys/boot/userboot/libstand \ sys/boot/userboot/test \ sys/boot/userboot/userboot \ .if ${MK_ZFS} != "no" DIRDEPS.amd64+= \ sys/boot/userboot/zfs \ .endif DIRDEPS.arm= ${_sys_boot_fdt} ${_sys_boot_efi} DIRDEPS.arm64= ${_sys_boot_fdt} ${_sys_boot_efi} DIRDEPS.i386= ${DIRDEPS.x86sys} sys/boot/efi/libefi DIRDEPS.powerpc= ${_sys_boot_fdt} sys/boot/libstand32 sys/boot/ofw sys/boot/uboot DIRDEPS.pc98= sys/boot/libstand32 DIRDEPS.sparc64= sys/boot/ofw ${_sys_boot_zfs} .endif DIRDEPS+= ${DIRDEPS.${MACHINE}:U} .include Index: projects/release-pkg/targets =================================================================== --- projects/release-pkg/targets (revision 295422) +++ projects/release-pkg/targets (revision 295423) Property changes on: projects/release-pkg/targets ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/targets:r293336-295422 Index: projects/release-pkg =================================================================== --- projects/release-pkg (revision 295422) +++ projects/release-pkg (revision 295423) Property changes on: projects/release-pkg ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r295394-295422