Index: projects/clang700-import/Makefile.inc1 =================================================================== --- projects/clang700-import/Makefile.inc1 (revision 341812) +++ projects/clang700-import/Makefile.inc1 (revision 341813) @@ -1,3364 +1,3372 @@ # # $FreeBSD$ # # Make command line options: # -DNO_CLEANDIR run ${MAKE} clean, instead of ${MAKE} cleandir # -DNO_CLEAN do not clean at all # -DDB_FROM_SRC use the user/group databases in src/etc instead of # the system database when installing. # -DNO_SHARE do not go into share subdir # -DKERNFAST define NO_KERNEL{CONFIG,CLEAN,OBJ} # -DNO_KERNELCONFIG do not run config in ${MAKE} buildkernel # -DNO_KERNELCLEAN do not run ${MAKE} clean in ${MAKE} buildkernel # -DNO_KERNELOBJ do not run ${MAKE} obj in ${MAKE} buildkernel # -DNO_PORTSUPDATE do not update ports in ${MAKE} update # -DNO_ROOT install without using root privilege # -DNO_DOCUPDATE do not update doc in ${MAKE} update # -DWITHOUT_CTF do not run the DTrace CTF conversion tools on built objects # LOCAL_DIRS="list of dirs" to add additional dirs to the SUBDIR list # LOCAL_ITOOLS="list of tools" to add additional tools to the ITOOLS list # LOCAL_LIB_DIRS="list of dirs" to add additional dirs to libraries target # LOCAL_MTREE="list of mtree files" to process to allow local directories # to be created before files are installed # LOCAL_TOOL_DIRS="list of dirs" to add additional dirs to the build-tools # list # LOCAL_XTOOL_DIRS="list of dirs" to add additional dirs to the # cross-tools target # METALOG="path to metadata log" to write permission and ownership # when NO_ROOT is set. (default: ${DESTDIR}/METALOG) # TARGET="machine" to crossbuild world for a different machine type # TARGET_ARCH= may be required when a TARGET supports multiple endians # BUILDENV_SHELL= shell to launch for the buildenv target (def:${SHELL}) # WORLD_FLAGS= additional flags to pass to make(1) during buildworld # KERNEL_FLAGS= additional flags to pass to make(1) during buildkernel # SUBDIR_OVERRIDE="list of dirs" to build rather than everything. # All libraries and includes, and some build tools will still build. # # The intended user-driven targets are: # buildworld - rebuild *everything*, including glue to help do upgrades # installworld- install everything built by "buildworld" # checkworld - run test suite on installed world # doxygen - build API documentation of the kernel # update - convenient way to update your source tree (eg: svn/svnup) # # Standard targets (not defined here) are documented in the makefiles in # /usr/share/mk. These include: # obj depend all install clean cleandepend cleanobj .if !defined(TARGET) || !defined(TARGET_ARCH) .error "Both TARGET and TARGET_ARCH must be defined." .endif .if make(showconfig) || make(test-system-*) _MKSHOWCONFIG= t .endif SRCDIR?= ${.CURDIR} LOCALBASE?= /usr/local # Cross toolchain changes must be in effect before bsd.compiler.mk # so that gets the right CC, and pass CROSS_TOOLCHAIN to submakes. .if defined(CROSS_TOOLCHAIN) .if exists(${LOCALBASE}/share/toolchains/${CROSS_TOOLCHAIN}.mk) .include "${LOCALBASE}/share/toolchains/${CROSS_TOOLCHAIN}.mk" .elif exists(${CROSS_TOOLCHAIN}) .include "${CROSS_TOOLCHAIN}" .else .error CROSS_TOOLCHAIN ${CROSS_TOOLCHAIN} not found .endif CROSSENV+=CROSS_TOOLCHAIN="${CROSS_TOOLCHAIN}" .endif .if defined(CROSS_TOOLCHAIN_PREFIX) CROSS_COMPILER_PREFIX?=${CROSS_TOOLCHAIN_PREFIX} .endif XCOMPILERS= CC CXX CPP .for COMPILER in ${XCOMPILERS} .if defined(CROSS_COMPILER_PREFIX) X${COMPILER}?= ${CROSS_COMPILER_PREFIX}${${COMPILER}} .else X${COMPILER}?= ${${COMPILER}} .endif .endfor # If a full path to an external cross compiler is given, don't build # a cross compiler. .if ${XCC:N${CCACHE_BIN}:M/*} MK_CLANG_BOOTSTRAP= no MK_GCC_BOOTSTRAP= no .endif # Pull in compiler metadata from buildworld/toolchain if possible to avoid # running CC from bsd.compiler.mk. .if make(installworld) || make(install) || make(distributeworld) || \ make(stageworld) .-include "${OBJTOP}/toolchain-metadata.mk" .if !defined(_LOADED_TOOLCHAIN_METADATA) .error A build is required first. You may have the wrong MAKEOBJDIRPREFIX set. .endif .endif # Pull in COMPILER_TYPE and COMPILER_FREEBSD_VERSION early. Pull it from the # tree to be friendlier to foreign OS builds. It's safe to do so unconditionally # here since we will always have the right make, unlike in src/Makefile # Don't include bsd.linker.mk yet until XBINUTILS is handled (after src.opts.mk) _NO_INCLUDE_LINKERMK= t # We also want the X_COMPILER* variables if we are using an external toolchain. _WANT_TOOLCHAIN_CROSS_VARS= t .include "share/mk/bsd.compiler.mk" .undef _NO_INCLUDE_LINKERMK .undef _WANT_TOOLCHAIN_CROSS_VARS # src.opts.mk depends on COMPILER_FEATURES .include "share/mk/src.opts.mk" .if ${TARGET} == ${MACHINE} TARGET_CPUTYPE?=${CPUTYPE} .else TARGET_CPUTYPE?= .endif .if !empty(TARGET_CPUTYPE) _TARGET_CPUTYPE=${TARGET_CPUTYPE} .else _TARGET_CPUTYPE=dummy .endif .if ${TARGET} == "arm" .if ${TARGET_ARCH:Marmv[67]*} != "" && ${TARGET_CPUTYPE:M*soft*} == "" TARGET_ABI= gnueabihf .else TARGET_ABI= gnueabi .endif .endif MACHINE_ABI?= unknown MACHINE_TRIPLE?=${MACHINE_ARCH:S/amd64/x86_64/:C/hf$//:S/mipsn32/mips64/}-${MACHINE_ABI}-freebsd13.0 TARGET_ABI?= unknown TARGET_TRIPLE?= ${TARGET_ARCH:S/amd64/x86_64/:C/hf$//:S/mipsn32/mips64/}-${TARGET_ABI}-freebsd13.0 KNOWN_ARCHES?= aarch64/arm64 \ amd64 \ arm \ armv6/arm \ armv7/arm \ i386 \ mips \ mipsel/mips \ mips64el/mips \ mipsn32el/mips \ mips64/mips \ mipsn32/mips \ mipshf/mips \ mipselhf/mips \ mips64elhf/mips \ mips64hf/mips \ powerpc \ powerpc64/powerpc \ powerpcspe/powerpc \ riscv64/riscv \ riscv64sf/riscv \ sparc64 .if ${TARGET} == ${TARGET_ARCH} _t= ${TARGET} .else _t= ${TARGET_ARCH}/${TARGET} .endif .for _t in ${_t} .if empty(KNOWN_ARCHES:M${_t}) .error Unknown target ${TARGET_ARCH}:${TARGET}. .endif .endfor # If all targets are disabled for system llvm then don't expect it to work # for cross-builds. .if !defined(TOOLS_PREFIX) && ${MK_LLVM_TARGET_ALL} == "no" && \ ${MACHINE} != ${TARGET} && ${MACHINE_ARCH} != ${TARGET_ARCH} && \ !make(showconfig) MK_SYSTEM_COMPILER= no MK_SYSTEM_LINKER= no .endif # Handle external binutils. .if defined(CROSS_TOOLCHAIN_PREFIX) CROSS_BINUTILS_PREFIX?=${CROSS_TOOLCHAIN_PREFIX} .endif # If we do not have a bootstrap binutils (because the in-tree one does not # support the target architecture), provide a default cross-binutils prefix. # This allows riscv64 builds, for example, to automatically use the # riscv64-binutils port or package. .if !make(showconfig) && !defined(_NO_INCLUDE_COMPILERMK) .if !empty(BROKEN_OPTIONS:MBINUTILS_BOOTSTRAP) && \ ${MK_LLD_BOOTSTRAP} == "no" && \ !defined(CROSS_BINUTILS_PREFIX) CROSS_BINUTILS_PREFIX=/usr/local/${TARGET_TRIPLE}/bin/ .if !exists(${CROSS_BINUTILS_PREFIX}) .error In-tree binutils does not support the ${TARGET_ARCH} architecture. Install the ${TARGET_ARCH}-binutils port or package or set CROSS_BINUTILS_PREFIX. .endif .endif .endif XBINUTILS= AS AR LD NM OBJCOPY RANLIB SIZE STRINGS .for BINUTIL in ${XBINUTILS} .if defined(CROSS_BINUTILS_PREFIX) && \ exists(${CROSS_BINUTILS_PREFIX}/${${BINUTIL}}) X${BINUTIL}?= ${CROSS_BINUTILS_PREFIX:C,/*$,,}/${${BINUTIL}} .else X${BINUTIL}?= ${${BINUTIL}} .endif .endfor # If a full path to an external linker is given, don't build lld. .if ${XLD:M/*} MK_LLD_BOOTSTRAP= no .endif # We also want the X_LINKER* variables if we are using an external toolchain. _WANT_TOOLCHAIN_CROSS_VARS= t .include "share/mk/bsd.linker.mk" .undef _WANT_TOOLCHAIN_CROSS_VARS # Begin WITH_SYSTEM_COMPILER / WITH_SYSTEM_LD # WITH_SYSTEM_COMPILER - Pull in needed values and make a decision. # Check if there is a local compiler that can satisfy as an external compiler. # Which compiler is expected to be used? .if ${MK_CLANG_BOOTSTRAP} == "yes" WANT_COMPILER_TYPE= clang .elif ${MK_GCC_BOOTSTRAP} == "yes" WANT_COMPILER_TYPE= gcc .else WANT_COMPILER_TYPE= .endif .if !defined(WANT_COMPILER_FREEBSD_VERSION) && !make(showconfig) && \ !make(test-system-linker) .if ${WANT_COMPILER_TYPE} == "clang" WANT_COMPILER_FREEBSD_VERSION_FILE= lib/clang/freebsd_cc_version.h WANT_COMPILER_FREEBSD_VERSION!= \ awk '$$2 == "FREEBSD_CC_VERSION" {printf("%d\n", $$3)}' \ ${SRCDIR}/${WANT_COMPILER_FREEBSD_VERSION_FILE} || echo unknown WANT_COMPILER_VERSION_FILE= lib/clang/include/clang/Basic/Version.inc WANT_COMPILER_VERSION!= \ awk '$$2 == "CLANG_VERSION" {split($$3, a, "."); print a[1] * 10000 + a[2] * 100 + a[3]}' \ ${SRCDIR}/${WANT_COMPILER_VERSION_FILE} || echo unknown .elif ${WANT_COMPILER_TYPE} == "gcc" WANT_COMPILER_FREEBSD_VERSION_FILE= gnu/usr.bin/cc/cc_tools/freebsd-native.h WANT_COMPILER_FREEBSD_VERSION!= \ awk '$$2 == "FBSD_CC_VER" {printf("%d\n", $$3)}' \ ${SRCDIR}/${WANT_COMPILER_FREEBSD_VERSION_FILE} || echo unknown WANT_COMPILER_VERSION_FILE= contrib/gcc/BASE-VER WANT_COMPILER_VERSION!= \ awk -F. '{print $$1 * 10000 + $$2 * 100 + $$3}' \ ${SRCDIR}/${WANT_COMPILER_VERSION_FILE} || echo unknown .endif .export WANT_COMPILER_FREEBSD_VERSION WANT_COMPILER_VERSION .endif # !defined(WANT_COMPILER_FREEBSD_VERSION) # It needs to be the same revision as we would build for the bootstrap. # If the expected vs CC is different then we can't skip. # GCC cannot be used for cross-arch yet. For clang we pass -target later if # TARGET_ARCH!=MACHINE_ARCH. .if ${MK_SYSTEM_COMPILER} == "yes" && \ defined(WANT_COMPILER_FREEBSD_VERSION) && \ (${MK_CLANG_BOOTSTRAP} == "yes" || ${MK_GCC_BOOTSTRAP} == "yes") && \ !make(xdev*) && \ ${X_COMPILER_TYPE} == ${WANT_COMPILER_TYPE} && \ (${X_COMPILER_TYPE} == "clang" || ${TARGET_ARCH} == ${MACHINE_ARCH}) && \ ${X_COMPILER_VERSION} == ${WANT_COMPILER_VERSION} && \ ${X_COMPILER_FREEBSD_VERSION} == ${WANT_COMPILER_FREEBSD_VERSION} # Everything matches, disable the bootstrap compiler. MK_CLANG_BOOTSTRAP= no MK_GCC_BOOTSTRAP= no USING_SYSTEM_COMPILER= yes .endif # ${WANT_COMPILER_TYPE} == ${COMPILER_TYPE} # WITH_SYSTEM_LD - Pull in needed values and make a decision. # Check if there is a local linker that can satisfy as an external linker. # Which linker is expected to be used? .if ${MK_LLD_BOOTSTRAP} == "yes" WANT_LINKER_TYPE= lld .elif ${MK_BINUTILS_BOOTSTRAP} == "yes" # Note that there's no support for bfd in WITH_SYSTEM_LINKER. WANT_LINKER_TYPE= bfd .else WANT_LINKER_TYPE= .endif .if !defined(WANT_LINKER_FREEBSD_VERSION) && !make(showconfig) && \ !make(test-system-compiler) .if ${WANT_LINKER_TYPE} == "lld" WANT_LINKER_FREEBSD_VERSION_FILE= lib/clang/include/lld/Common/Version.inc WANT_LINKER_FREEBSD_VERSION!= \ awk '$$2 == "LLD_REVISION_STRING" {gsub(/"/, "", $$3); print $$3}' \ ${SRCDIR}/${WANT_LINKER_FREEBSD_VERSION_FILE} || echo unknown WANT_LINKER_VERSION_FILE= lib/clang/include/lld/Common/Version.inc WANT_LINKER_VERSION!= \ awk '$$2 == "LLD_VERSION" {split($$3, a, "."); print a[1] * 10000 + a[2] * 100 + a[3]}' \ ${SRCDIR}/${WANT_LINKER_VERSION_FILE} || echo unknown .else WANT_LINKER_FREEBSD_VERSION_FILE= WANT_LINKER_FREEBSD_VERSION= .endif .export WANT_LINKER_FREEBSD_VERSION WANT_LINKER_VERSION .endif # !defined(WANT_LINKER_FREEBSD_VERSION) .if ${MK_SYSTEM_LINKER} == "yes" && \ defined(WANT_LINKER_FREEBSD_VERSION) && \ (${MK_LLD_BOOTSTRAP} == "yes") && \ !make(xdev*) && \ ${X_LINKER_TYPE} == ${WANT_LINKER_TYPE} && \ ${X_LINKER_VERSION} == ${WANT_LINKER_VERSION} && \ ${X_LINKER_FREEBSD_VERSION} == ${WANT_LINKER_FREEBSD_VERSION} # Everything matches, disable the bootstrap linker. MK_LLD_BOOTSTRAP= no USING_SYSTEM_LINKER= yes .endif # ${WANT_LINKER_TYPE} == ${LINKER_TYPE} # WITH_SYSTEM_COMPILER / WITH_SYSTEM_LINKER - Handle defaults and debug. USING_SYSTEM_COMPILER?= no USING_SYSTEM_LINKER?= no TEST_SYSTEM_COMPILER_VARS= \ USING_SYSTEM_COMPILER MK_SYSTEM_COMPILER \ MK_CROSS_COMPILER MK_CLANG_BOOTSTRAP MK_GCC_BOOTSTRAP \ WANT_COMPILER_TYPE WANT_COMPILER_VERSION WANT_COMPILER_VERSION_FILE \ WANT_COMPILER_FREEBSD_VERSION WANT_COMPILER_FREEBSD_VERSION_FILE \ CC COMPILER_TYPE COMPILER_FEATURES COMPILER_VERSION \ COMPILER_FREEBSD_VERSION \ XCC X_COMPILER_TYPE X_COMPILER_FEATURES X_COMPILER_VERSION \ X_COMPILER_FREEBSD_VERSION TEST_SYSTEM_LINKER_VARS= \ USING_SYSTEM_LINKER MK_SYSTEM_LINKER \ MK_LLD_BOOTSTRAP MK_BINUTILS_BOOTSTRAP \ WANT_LINKER_TYPE WANT_LINKER_VERSION WANT_LINKER_VERSION_FILE \ WANT_LINKER_FREEBSD_VERSION WANT_LINKER_FREEBSD_VERSION_FILE \ LD LINKER_TYPE LINKER_FEATURES LINKER_VERSION \ LINKER_FREEBSD_VERSION \ XLD X_LINKER_TYPE X_LINKER_FEATURES X_LINKER_VERSION \ X_LINKER_FREEBSD_VERSION .for _t in compiler linker test-system-${_t}: .PHONY .for v in ${TEST_SYSTEM_${_t:tu}_VARS} ${_+_}@printf "%-35s= %s\n" "${v}" "${${v}}" .endfor .endfor .if (make(buildworld) || make(buildkernel) || make(kernel-toolchain) || \ make(toolchain) || make(_cross-tools)) .if ${USING_SYSTEM_COMPILER} == "yes" .info SYSTEM_COMPILER: Determined that CC=${CC} matches the source tree. Not bootstrapping a cross-compiler. .elif ${MK_CLANG_BOOTSTRAP} == "yes" .info SYSTEM_COMPILER: libclang will be built for bootstrapping a cross-compiler. .endif .if ${USING_SYSTEM_LINKER} == "yes" .info SYSTEM_LINKER: Determined that LD=${LD} matches the source tree. Not bootstrapping a cross-linker. .elif ${MK_LLD_BOOTSTRAP} == "yes" .info SYSTEM_LINKER: libclang will be built for bootstrapping a cross-linker. .endif .endif # End WITH_SYSTEM_COMPILER / WITH_SYSTEM_LD # Store some compiler metadata for use in installworld where we don't # want to invoke CC at all. _TOOLCHAIN_METADATA_VARS= COMPILER_VERSION \ COMPILER_TYPE \ COMPILER_FEATURES \ COMPILER_FREEBSD_VERSION \ LINKER_VERSION \ LINKER_FEATURES \ LINKER_TYPE \ LINKER_FREEBSD_VERSION toolchain-metadata.mk: .PHONY .META @: > ${.TARGET} @echo ".info Using cached toolchain metadata from build at $$(hostname) on $$(date)" \ > ${.TARGET} @echo "_LOADED_TOOLCHAIN_METADATA=t" >> ${.TARGET} .for v in ${_TOOLCHAIN_METADATA_VARS} @echo "${v}=${${v}}" >> ${.TARGET} @echo "X_${v}=${X_${v}}" >> ${.TARGET} .endfor @echo ".export ${_TOOLCHAIN_METADATA_VARS}" >> ${.TARGET} @echo ".export ${_TOOLCHAIN_METADATA_VARS:C,^,X_,}" >> ${.TARGET} # We must do lib/ and libexec/ before bin/ in case of a mid-install error to # keep the users system reasonably usable. For static->dynamic root upgrades, # we don't want to install a dynamic binary without rtld and the needed # libraries. More commonly, for dynamic root, we don't want to install a # binary that requires a newer library version that hasn't been installed yet. # This ordering is not a guarantee though. The only guarantee of a working # system here would require fine-grained ordering of all components based # on their dependencies. .if !empty(SUBDIR_OVERRIDE) SUBDIR= ${SUBDIR_OVERRIDE} .else SUBDIR= lib libexec # Add LOCAL_LIB_DIRS, but only if they will not be picked up as a SUBDIR # of a LOCAL_DIRS directory. This allows LOCAL_DIRS=foo and # LOCAL_LIB_DIRS=foo/lib to behave as expected. .for _DIR in ${LOCAL_DIRS:M*/} ${LOCAL_DIRS:N*/:S|$|/|} _REDUNDANT_LIB_DIRS+= ${LOCAL_LIB_DIRS:M${_DIR}*} .endfor .for _DIR in ${LOCAL_LIB_DIRS} .if ${_DIR} == ".WAIT" || (empty(_REDUNDANT_LIB_DIRS:M${_DIR}) && exists(${.CURDIR}/${_DIR}/Makefile)) SUBDIR+= ${_DIR} .endif .endfor .if !defined(NO_ROOT) && (make(installworld) || make(install)) # Ensure libraries are installed before progressing. SUBDIR+=.WAIT .endif SUBDIR+=bin .if ${MK_CDDL} != "no" SUBDIR+=cddl .endif SUBDIR+=gnu include .if ${MK_KERBEROS} != "no" SUBDIR+=kerberos5 .endif .if ${MK_RESCUE} != "no" SUBDIR+=rescue .endif SUBDIR+=sbin .if ${MK_CRYPT} != "no" SUBDIR+=secure .endif .if !defined(NO_SHARE) SUBDIR+=share .endif .if ${MK_BOOT} != "no" SUBDIR+=stand .endif SUBDIR+=sys usr.bin usr.sbin .if ${MK_TESTS} != "no" SUBDIR+= tests .endif # Local directories are built in parallel with the base system directories. # Users may insert a .WAIT directive at the beginning or elsewhere within # the LOCAL_DIRS and LOCAL_LIB_DIRS lists as needed. .for _DIR in ${LOCAL_DIRS} .if ${_DIR} == ".WAIT" || exists(${.CURDIR}/${_DIR}/Makefile) SUBDIR+= ${_DIR} .endif .endfor # We must do etc/ last as it hooks into building the man whatis file # by calling 'makedb' in share/man. This is only relevant for # install/distribute so they build the whatis file after every manpage is # installed. .if make(installworld) || make(install) SUBDIR+=.WAIT .endif SUBDIR+=etc .endif # !empty(SUBDIR_OVERRIDE) .if defined(NOCLEAN) .warning NOCLEAN option is deprecated. Use NO_CLEAN instead. NO_CLEAN= ${NOCLEAN} .endif .if defined(NO_CLEANDIR) CLEANDIR= clean cleandepend .else CLEANDIR= cleandir .endif .if defined(WORLDFAST) NO_CLEAN= t NO_OBJWALK= t .endif .if ${MK_META_MODE} == "yes" # If filemon is used then we can rely on the build being incremental-safe. # The .meta files will also track the build command and rebuild should # it change. .if empty(.MAKE.MODE:Mnofilemon) NO_CLEAN= t .endif .endif .if defined(NO_OBJWALK) || ${MK_AUTO_OBJ} == "yes" NO_OBJWALK= t NO_KERNELOBJ= t .endif .if !defined(NO_OBJWALK) _obj= obj .endif LOCAL_TOOL_DIRS?= PACKAGEDIR?= ${DESTDIR}/${DISTDIR} .if empty(SHELL:M*csh*) BUILDENV_SHELL?=${SHELL} .else BUILDENV_SHELL?=/bin/sh .endif .if !defined(_MKSHOWCONFIG) .if !defined(SVN_CMD) || empty(SVN_CMD) . for _P in /usr/bin /usr/local/bin . for _S in svn svnlite . if exists(${_P}/${_S}) SVN_CMD= ${_P}/${_S} . endif . endfor . endfor .export SVN_CMD .endif SVNFLAGS?= -r HEAD .if !defined(VCS_REVISION) || empty(VCS_REVISION) .if !defined(SVNVERSION_CMD) || empty(SVNVERSION_CMD) . for _D in ${PATH:S,:, ,g} . if exists(${_D}/svnversion) SVNVERSION_CMD?=${_D}/svnversion . endif . if exists(${_D}/svnliteversion) SVNVERSION_CMD?=${_D}/svnliteversion . endif . endfor .endif _VCS_REVISION?= $$(eval ${SVNVERSION_CMD} ${SRCDIR}) . if !empty(_VCS_REVISION) VCS_REVISION= $$(echo r${_VCS_REVISION}) . endif .export VCS_REVISION .endif .if !defined(OSRELDATE) .if exists(/usr/include/osreldate.h) OSRELDATE!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ /usr/include/osreldate.h .else OSRELDATE= 0 .endif .export OSRELDATE .endif # Set VERSION for CTFMERGE to use via the default CTFFLAGS=-L VERSION. .if !defined(_REVISION) _REVISION!= ${MAKE} -C ${SRCDIR}/release MK_AUTO_OBJ=no -V REVISION .export _REVISION .endif .if !defined(_BRANCH) _BRANCH!= ${MAKE} -C ${SRCDIR}/release MK_AUTO_OBJ=no -V BRANCH .export _BRANCH .endif .if !defined(SRCRELDATE) SRCRELDATE!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ ${SRCDIR}/sys/sys/param.h .export SRCRELDATE .endif .if !defined(VERSION) VERSION= FreeBSD ${_REVISION}-${_BRANCH:C/-p[0-9]+$//} ${TARGET_ARCH} ${SRCRELDATE} .export VERSION .endif .if !defined(PKG_VERSION) .if ${_BRANCH:MSTABLE*} || ${_BRANCH:MCURRENT*} || ${_BRANCH:MALPHA*} TIMENOW= %Y%m%d%H%M%S EXTRA_REVISION= .s${TIMENOW:gmtime} .endif .if ${_BRANCH:M*-p*} EXTRA_REVISION= _${_BRANCH:C/.*-p([0-9]+$)/\1/} .endif PKG_VERSION= ${_REVISION}${EXTRA_REVISION} .endif .endif # !defined(_MKSHOWCONFIG) .if !defined(_MKSHOWCONFIG) _CPUTYPE!= MAKEFLAGS= CPUTYPE=${_TARGET_CPUTYPE} ${MAKE} -f /dev/null \ -m ${.CURDIR}/share/mk MK_AUTO_OBJ=no -V CPUTYPE .if ${_CPUTYPE} != ${_TARGET_CPUTYPE} .error CPUTYPE global should be set with ?=. .endif .endif .if make(buildworld) BUILD_ARCH!= uname -p .if ${MACHINE_ARCH} != ${BUILD_ARCH} .error To cross-build, set TARGET_ARCH. .endif .endif WORLDTMP?= ${OBJTOP}/tmp BPATH= ${CCACHE_WRAPPER_PATH_PFX}${WORLDTMP}/legacy/usr/sbin:${WORLDTMP}/legacy/usr/bin:${WORLDTMP}/legacy/bin XPATH= ${WORLDTMP}/usr/sbin:${WORLDTMP}/usr/bin # When building we want to find the cross tools before the host tools in ${BPATH}. # We also need to add UNIVERSE_TOOLCHAIN_PATH so that we can find the shared # toolchain files (clang, lld, etc.) during make universe/tinderbox STRICTTMPPATH= ${XPATH}:${BPATH}:${UNIVERSE_TOOLCHAIN_PATH} # We should not be using tools from /usr/bin accidentally since this could cause # the build to break on other systems that don't have that tool. For now we # still allow using the old behaviour (inheriting $PATH) if # BUILD_WITH_STRICT_TMPPATH is set to 0 but this will eventually be removed. # Currently strict $PATH can cause build failures and does not work yet with # USING_SYSTEM_LINKER/USING_SYSTEM_COMPILER. Once these issues have been # resolved it will be turned on by default. BUILD_WITH_STRICT_TMPPATH?=0 .if ${BUILD_WITH_STRICT_TMPPATH} != 0 TMPPATH= ${STRICTTMPPATH} .else TMPPATH= ${STRICTTMPPATH}:${PATH} .endif # # Avoid running mktemp(1) unless actually needed. # It may not be functional, e.g., due to new ABI # when in the middle of installing over this system. # .if make(distributeworld) || make(installworld) || make(stageworld) .if ${BUILD_WITH_STRICT_TMPPATH} != 0 MKTEMP=${WORLDTMP}/legacy/usr/bin/mktemp .if !exists(${MKTEMP}) .error "mktemp binary doesn't exist in expected location: ${MKTEMP}" .endif .else MKTEMP=mktemp .endif INSTALLTMP!= ${MKTEMP} -d -u -t install .endif .if make(stagekernel) || make(distributekernel) TAGS+= kernel PACKAGE= kernel .endif # # Building a world goes through the following stages # # 1. legacy stage [BMAKE] # This stage is responsible for creating compatibility # shims that are needed by the bootstrap-tools, # build-tools and cross-tools stages. These are generally # APIs that tools from one of those three stages need to # build that aren't present on the host. # 1. bootstrap-tools stage [BMAKE] # This stage is responsible for creating programs that # are needed for backward compatibility reasons. They # are not built as cross-tools. # 2. build-tools stage [TMAKE] # This stage is responsible for creating the object # tree and building any tools that are needed during # the build process. Some programs are listed during # this phase because they build binaries to generate # files needed to build these programs. This stage also # builds the 'build-tools' target rather than 'all'. # 3. cross-tools stage [XMAKE] # This stage is responsible for creating any tools that # are needed for building the system. A cross-compiler is one # of them. This differs from build tools in two ways: # 1. the 'all' target is built rather than 'build-tools' # 2. these tools are installed into TMPPATH for stage 4. # 4. world stage [WMAKE] # This stage actually builds the world. # 5. install stage (optional) [IMAKE] # This stage installs a previously built world. # BOOTSTRAPPING?= 0 # Keep these in sync MINIMUM_SUPPORTED_OSREL?= 1002501 MINIMUM_SUPPORTED_REL?= 10.3 # Common environment for world related stages CROSSENV+= \ MACHINE_ARCH=${TARGET_ARCH} \ MACHINE=${TARGET} \ CPUTYPE=${TARGET_CPUTYPE} .if ${MK_META_MODE} != "no" # Don't rebuild build-tools targets during normal build. CROSSENV+= BUILD_TOOLS_META=.NOMETA .endif .if defined(TARGET_CFLAGS) CROSSENV+= ${TARGET_CFLAGS} .endif BOOTSTRAPPING_OSRELDATE?=${OSRELDATE} # bootstrap-tools stage BMAKEENV= INSTALL="sh ${.CURDIR}/tools/install.sh" \ TOOLS_PREFIX=${TOOLS_PREFIX_UNDEF:U${WORLDTMP}} \ PATH=${BPATH}:${PATH} \ WORLDTMP=${WORLDTMP} \ MAKEFLAGS="-m ${.CURDIR}/tools/build/mk ${.MAKEFLAGS}" # need to keep this in sync with targets/pseudo/bootstrap-tools/Makefile BSARGS= DESTDIR= \ OBJTOP='${WORLDTMP}/obj-tools' \ OBJROOT='$${OBJTOP}/' \ MAKEOBJDIRPREFIX= \ BOOTSTRAPPING=${BOOTSTRAPPING_OSRELDATE} \ BWPHASE=${.TARGET:C,^_,,} \ SSP_CFLAGS= \ MK_HTML=no NO_LINT=yes MK_MAN=no \ -DNO_PIC MK_PROFILE=no -DNO_SHARED \ -DNO_CPU_CFLAGS MK_WARNS=no MK_CTF=no \ MK_CLANG_EXTRAS=no MK_CLANG_FULL=no \ MK_LLDB=no MK_RETPOLINE=no MK_TESTS=no \ MK_INCLUDES=yes BMAKE= \ ${BMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ ${BSARGS} .if empty(.MAKEOVERRIDES:MMK_LLVM_TARGET_ALL) BMAKE+= MK_LLVM_TARGET_ALL=no .endif # build-tools stage TMAKE= \ ${BMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ DESTDIR= \ BOOTSTRAPPING=${BOOTSTRAPPING_OSRELDATE} \ BWPHASE=${.TARGET:C,^_,,} \ SSP_CFLAGS= \ -DNO_LINT \ -DNO_CPU_CFLAGS MK_WARNS=no MK_CTF=no \ MK_CLANG_EXTRAS=no MK_CLANG_FULL=no \ MK_LLDB=no MK_RETPOLINE=no MK_TESTS=no # cross-tools stage # TOOLS_PREFIX set in BMAKE XMAKE= ${BMAKE} \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ MK_GDB=no MK_TESTS=no # kernel-tools stage KTMAKEENV= INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${BPATH}:${PATH} \ WORLDTMP=${WORLDTMP} KTMAKE= \ TOOLS_PREFIX=${TOOLS_PREFIX_UNDEF:U${WORLDTMP}} \ ${KTMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ DESTDIR= \ OBJTOP='${WORLDTMP}/obj-kernel-tools' \ OBJROOT='$${OBJTOP}/' \ MAKEOBJDIRPREFIX= \ BOOTSTRAPPING=${BOOTSTRAPPING_OSRELDATE} \ SSP_CFLAGS= \ MK_HTML=no -DNO_LINT MK_MAN=no \ -DNO_PIC MK_PROFILE=no -DNO_SHARED \ -DNO_CPU_CFLAGS MK_RETPOLINE=no MK_WARNS=no MK_CTF=no # world stage WMAKEENV= ${CROSSENV} \ INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${TMPPATH} \ SYSROOT=${WORLDTMP} # make hierarchy HMAKE= PATH=${TMPPATH} ${MAKE} LOCAL_MTREE=${LOCAL_MTREE:Q} .if defined(NO_ROOT) HMAKE+= PATH=${TMPPATH} METALOG=${METALOG} -DNO_ROOT .endif CROSSENV+= CC="${XCC} ${XCFLAGS}" CXX="${XCXX} ${XCXXFLAGS} ${XCFLAGS}" \ CPP="${XCPP} ${XCFLAGS}" \ AS="${XAS}" AR="${XAR}" LD="${XLD}" LLVM_LINK="${XLLVM_LINK}" \ NM=${XNM} OBJCOPY="${XOBJCOPY}" \ RANLIB=${XRANLIB} STRINGS=${XSTRINGS} \ SIZE="${XSIZE}" .if defined(CROSS_BINUTILS_PREFIX) && exists(${CROSS_BINUTILS_PREFIX}) # In the case of xdev-build tools, CROSS_BINUTILS_PREFIX won't be a # directory, but the compiler will look in the right place for its # tools so we don't need to tell it where to look. BFLAGS+= -B${CROSS_BINUTILS_PREFIX} .endif # The internal bootstrap compiler has a default sysroot set by TOOLS_PREFIX # and target set by TARGET/TARGET_ARCH. However, there are several needs to # always pass an explicit --sysroot and -target. # - External compiler needs sysroot and target flags. # - External ld needs sysroot. # - To be clear about the use of a sysroot when using the internal compiler. # - Easier debugging. # - Allowing WITH_SYSTEM_COMPILER+WITH_META_MODE to work together due to # the flip-flopping build command when sometimes using external and # sometimes using internal. # - Allow using lld which has no support for default paths. .if !defined(CROSS_BINUTILS_PREFIX) || !exists(${CROSS_BINUTILS_PREFIX}) BFLAGS+= -B${WORLDTMP}/usr/bin .endif .if ${WANT_COMPILER_TYPE} == gcc || \ (defined(X_COMPILER_TYPE) && ${X_COMPILER_TYPE} == gcc) .elif ${WANT_COMPILER_TYPE} == clang || \ (defined(X_COMPILER_TYPE) && ${X_COMPILER_TYPE} == clang) XCFLAGS+= -target ${TARGET_TRIPLE} .endif XCFLAGS+= --sysroot=${WORLDTMP} .if !empty(BFLAGS) XCFLAGS+= ${BFLAGS} .endif .if ${MK_LIB32} != "no" && (${TARGET_ARCH} == "amd64" || \ ${TARGET_ARCH} == "powerpc64" || ${TARGET_ARCH:Mmips64*} != "") LIBCOMPAT= 32 .include "Makefile.libcompat" .elif ${MK_LIBSOFT} != "no" && ${TARGET_ARCH:Marmv[67]*} != "" LIBCOMPAT= SOFT .include "Makefile.libcompat" .endif # META_MODE normally ignores host file changes since every build updates # timestamps (see NO_META_IGNORE_HOST in sys.mk). There are known times # when the ABI breaks though that we want to force rebuilding WORLDTMP # to get updated host tools. .if ${MK_META_MODE} == "yes" && defined(NO_CLEAN) && \ !defined(NO_META_IGNORE_HOST) && !defined(NO_META_IGNORE_HOST_HEADERS) && \ !defined(_MKSHOWCONFIG) # r318736 - ino64 major ABI breakage META_MODE_BAD_ABI_VERS+= 1200031 .if !defined(OBJDIR_HOST_OSRELDATE) .if exists(${OBJTOP}/host-osreldate.h) OBJDIR_HOST_OSRELDATE!= \ awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ ${OBJTOP}/host-osreldate.h .elif exists(${WORLDTMP}/usr/include/osreldate.h) OBJDIR_HOST_OSRELDATE= 0 .endif .export OBJDIR_HOST_OSRELDATE .endif # Note that this logic is the opposite of normal BOOTSTRAP handling. We want # to compare the WORLDTMP's OSRELDATE to the host's OSRELDATE. If the WORLDTMP # is older than the ABI-breakage OSRELDATE of the HOST then we rebuild. .if defined(OBJDIR_HOST_OSRELDATE) .for _ver in ${META_MODE_BAD_ABI_VERS} .if ${OSRELDATE} >= ${_ver} && ${OBJDIR_HOST_OSRELDATE} < ${_ver} _meta_mode_need_rebuild= ${_ver} .endif .endfor .if defined(_meta_mode_need_rebuild) .info META_MODE: Rebuilding host tools due to ABI breakage in __FreeBSD_version ${_meta_mode_need_rebuild}. NO_META_IGNORE_HOST_HEADERS= 1 .export NO_META_IGNORE_HOST_HEADERS .endif # defined(_meta_mode_need_rebuild) .endif # defined(OBJDIR_HOST_OSRELDATE) .endif # ${MK_META_MODE} == "yes" && defined(NO_CLEAN) ... # This is only used for META_MODE+filemon to track what the oldest # __FreeBSD_version is in WORLDTMP. This purposely does NOT have # a make dependency on /usr/include/osreldate.h as the file should # only be copied when it is missing or meta mode determines it has changed. # Since host files are normally ignored without NO_META_IGNORE_HOST # the file will never be updated unless that flag is specified. This # allows tracking the oldest osreldate to force rebuilds via # META_MODE_BADABI_REVS above. host-osreldate.h: # DO NOT ADD /usr/include/osreldate.h here @cp -f /usr/include/osreldate.h ${.TARGET} WMAKE= ${WMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ BWPHASE=${.TARGET:C,^_,,} \ DESTDIR=${WORLDTMP} IMAKEENV= ${CROSSENV} IMAKE= ${IMAKEENV} ${MAKE} -f Makefile.inc1 \ ${IMAKE_INSTALL} ${IMAKE_MTREE} .if empty(.MAKEFLAGS:M-n) IMAKEENV+= PATH=${STRICTTMPPATH}:${INSTALLTMP} \ LD_LIBRARY_PATH=${INSTALLTMP} \ PATH_LOCALE=${INSTALLTMP}/locale IMAKE+= __MAKE_SHELL=${INSTALLTMP}/sh .else IMAKEENV+= PATH=${TMPPATH}:${INSTALLTMP} .endif # When generating install media, do not allow user and group information from # the build host to affect the contents of the distribution. .if make(distributeworld) || make(distrib-dirs) || make(distribution) DB_FROM_SRC= yes .endif .if defined(DB_FROM_SRC) INSTALLFLAGS+= -N ${.CURDIR}/etc MTREEFLAGS+= -N ${.CURDIR}/etc .endif _INSTALL_DDIR= ${DESTDIR}/${DISTDIR} INSTALL_DDIR= ${_INSTALL_DDIR:S://:/:g:C:/$::} .if defined(NO_ROOT) METALOG?= ${DESTDIR}/${DISTDIR}/METALOG METALOG:= ${METALOG:C,//+,/,g} IMAKE+= -DNO_ROOT METALOG=${METALOG} INSTALLFLAGS+= -U -M ${METALOG} -D ${INSTALL_DDIR} MTREEFLAGS+= -W .endif .if defined(BUILD_PKGS) INSTALLFLAGS+= -h sha256 .endif .if defined(DB_FROM_SRC) || defined(NO_ROOT) IMAKE_INSTALL= INSTALL="install ${INSTALLFLAGS}" IMAKE_MTREE= MTREE_CMD="mtree ${MTREEFLAGS}" .endif DESTDIR_MTREEFLAGS= -deU # When creating worldtmp we don't need to set the directories as owned by root # so we also pass -W WORLDTMP_MTREEFLAGS= -deUW .if defined(NO_ROOT) # When building with -DNO_ROOT we shouldn't be changing the directories # that are created by mtree to be owned by root/wheel. DESTDIR_MTREEFLAGS+= -W .endif MTREE?= mtree .if ${BUILD_WITH_STRICT_TMPPATH} != 0 MTREE= ${WORLDTMP}/legacy/usr/sbin/mtree .endif WORLDTMP_MTREE= ${MTREE} ${WORLDTMP_MTREEFLAGS} DESTDIR_MTREE= ${MTREE} ${DESTDIR_MTREEFLAGS} # kernel stage KMAKEENV= ${WMAKEENV:NSYSROOT=*} KMAKE= ${KMAKEENV} ${MAKE} ${.MAKEFLAGS} ${KERNEL_FLAGS} KERNEL=${INSTKERNNAME} # # buildworld # # Attempt to rebuild the entire system, with reasonable chance of # success, regardless of how old your existing system is. # _sanity_check: .PHONY .MAKE .if ${.CURDIR:C/[^,]//g} != "" # The m4 build of sendmail files doesn't like it if ',' is used # anywhere in the path of it's files. @echo @echo "*** Error: path to source tree contains a comma ','" @echo @false .elif ${.CURDIR:M*\:*} != "" # Using ':' leaks into PATH and breaks finding cross-tools. @echo @echo "*** Error: path to source tree contains a colon ':'" @echo @false .endif # Our current approach to dependency tracking cannot cope with certain source # tree changes, particularly with respect to removing source files and # replacing generated files. Handle these cases here in an ad-hoc fashion. _cleanobj_fast_depend_hack: .PHONY # Syscall stubs rewritten in C and obsolete MD assembly implementations # Date SVN Rev Syscalls # 20180404 r332048 sigreturn # 20180405 r332080 shmat # 20180406 r332119 setlogin # 20180411 r332443 exect # 20180525 r334224 vadvise # 20180604 r334626 brk sbrk .for f in brk exect sbrk setlogin shmat sigreturn vadvise @if [ -e "${OBJTOP}/lib/libc/.depend.${f}.o" ] && \ egrep -qw '${f}\.[sS]' ${OBJTOP}/lib/libc/.depend.${f}.o; then \ echo "Removing stale dependencies for ${f} syscall wrappers"; \ rm -f ${OBJTOP}/lib/libc/.depend.${f}.* \ ${LIBCOMPAT:D${LIBCOMPAT_OBJTOP}/lib/libc/.depend.${f}.*}; \ fi .endfor # 20181013 r339348 bcopy reimplemented as .c .for f in bcopy memcpy memmove @if [ -e "${OBJTOP}/lib/libc/.depend.${f}.o" ] && \ egrep -qw 'bcopy\.[sS]' ${OBJTOP}/lib/libc/.depend.${f}.o; then \ echo "Removing stale dependencies for bcopy"; \ rm -f ${OBJTOP}/lib/libc/.depend.${f}.* \ ${LIBCOMPAT:D${LIBCOMPAT_OBJTOP}/lib/libc/.depend.${f}.*}; \ fi .endfor # 20181115 r340463 bzero reimplemented as .c @if [ -e "${OBJTOP}/lib/libc/.depend.bzero.o" ] && \ egrep -qw 'bzero\.[sS]' ${OBJTOP}/lib/libc/.depend.bzero.o; then \ echo "Removing stale dependencies for bzero"; \ rm -f ${OBJTOP}/lib/libc/.depend.bzero.* \ ${LIBCOMPAT:D${LIBCOMPAT_OBJTOP}/lib/libc/.depend.bzero.*}; \ fi # 20181009 track migration from ntp's embedded libevent to updated one @if [ -e "${OBJTOP}/usr.sbin/ntp/libntpevent/.depend.bufferevent_openssl.o" ] && \ egrep -q 'contrib/ntp/sntp/libevent/bufferevent_openssl.c' \ ${OBJTOP}/usr.sbin/ntp/libntpevent/.depend.bufferevent_openssl.o ; then \ echo "Removing stale libevent dependencies"; \ rm -f ${OBJTOP}/usr.sbin/ntp/libntpevent/.depend.*; \ fi +# 20181209 r341759 track migration across wpa update + @if [ -e "${OBJTOP}/usr.sbin/wpa/wpa_supplicant/.depend.rrm.o" ] && \ + egrep -q 'src/ap/rrm.c' \ + ${OBJTOP}/usr.sbin/wpa/wpa_supplicant/.depend.rrm.o; then \ + echo "Removing stale wpa dependencies and objects"; \ + rm -f ${OBJTOP}/usr.sbin/wpa/*/.depend*; \ + fi + _worldtmp: .PHONY @echo @echo "--------------------------------------------------------------" @echo ">>> Rebuilding the temporary build tree" @echo "--------------------------------------------------------------" .if !defined(NO_CLEAN) rm -rf ${WORLDTMP} .else # Note: for delete-old we need to set $PATH to also include the host $PATH # since otherwise a partial build with missing symlinks in ${WORLDTMP}/legacy/ # will fail to run due to missing binaries. $WMAKE sets PATH to only ${TMPPATH} # so we remove that assingnment from $WMAKE and prepend the new $PATH ${_+_}@if [ -e "${WORLDTMP}" ]; then \ echo ">>> Deleting stale files in build tree..."; \ cd ${.CURDIR}; env PATH=${TMPPATH}:${PATH} ${WMAKE:NPATH=*} \ _NO_INCLUDE_COMPILERMK=t -DBATCH_DELETE_OLD_FILES delete-old \ delete-old-libs >/dev/null; \ fi rm -rf ${WORLDTMP}/legacy/usr/include .if ${USING_SYSTEM_COMPILER} == "yes" .for cc in cc c++ if [ -x ${WORLDTMP}/usr/bin/${cc} ]; then \ inum=$$(stat -f %i ${WORLDTMP}/usr/bin/${cc}); \ find ${WORLDTMP}/usr/bin -inum $${inum} -delete; \ fi .endfor .endif # ${USING_SYSTEM_COMPILER} == "yes" .if ${USING_SYSTEM_LINKER} == "yes" @rm -f ${WORLDTMP}/usr/bin/ld ${WORLDTMP}/usr/bin/ld.lld .endif # ${USING_SYSTEM_LINKER} == "yes" .endif # !defined(NO_CLEAN) @mkdir -p ${WORLDTMP} @touch ${WORLDTMP}/${.TARGET} # We can't use mtree to create the worldtmp directories since it may not be # available on the target system (this happens e.g. when building on non-FreeBSD) cd ${.CURDIR}/tools/build; \ ${MAKE} DIRPRFX=tools/build/ DESTDIR=${WORLDTMP}/legacy installdirs # In order to build without inheriting $PATH we need to add symlinks to the host # tools in $WORLDTMP for the tools that we don't build during bootstrap-tools cd ${.CURDIR}/tools/build; \ ${MAKE} DIRPRFX=tools/build/ DESTDIR=${WORLDTMP}/legacy host-symlinks _legacy: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1.1: legacy release compatibility shims" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${BMAKE} legacy _bootstrap-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1.2: bootstrap tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${BMAKE} bootstrap-tools mkdir -p ${WORLDTMP}/usr ${WORLDTMP}/lib/casper ${WORLDTMP}/lib/geom ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${WORLDTMP}/usr >/dev/null ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${WORLDTMP}/usr/include >/dev/null ln -sf ${.CURDIR}/sys ${WORLDTMP} .if ${MK_DEBUG_FILES} != "no" ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.debug.dist \ -p ${WORLDTMP}/usr/lib >/dev/null .endif .for _mtree in ${LOCAL_MTREE} ${WORLDTMP_MTREE} -f ${.CURDIR}/${_mtree} -p ${WORLDTMP} > /dev/null .endfor _cleanobj: .if !defined(NO_CLEAN) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.1: cleaning up the object tree" @echo "--------------------------------------------------------------" # Avoid including bsd.compiler.mk in clean and obj with _NO_INCLUDE_COMPILERMK # since the restricted $PATH might not contain a valid cc binary ${_+_}cd ${.CURDIR}; ${WMAKE} _NO_INCLUDE_COMPILERMK=t ${CLEANDIR} .if defined(LIBCOMPAT) ${_+_}cd ${.CURDIR}; ${LIBCOMPATWMAKE} _NO_INCLUDE_COMPILERMK=t -f Makefile.inc1 ${CLEANDIR} .endif .else ${_+_}cd ${.CURDIR}; ${WMAKE} _NO_INCLUDE_COMPILERMK=t _cleanobj_fast_depend_hack .endif # !defined(NO_CLEAN) _obj: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.2: rebuilding the object tree" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} _NO_INCLUDE_COMPILERMK=t obj _build-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.3: build tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${TMAKE} build-tools _cross-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3: cross tools" @echo "--------------------------------------------------------------" @rm -f ${OBJTOP}/toolchain-metadata.mk ${_+_}cd ${.CURDIR}; ${XMAKE} cross-tools ${_+_}cd ${.CURDIR}; ${XMAKE} kernel-tools _build-metadata: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3.1: recording build metadata" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} toolchain-metadata.mk ${_+_}cd ${.CURDIR}; ${WMAKE} host-osreldate.h _includes: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.1: building includes" @echo "--------------------------------------------------------------" # Special handling for SUBDIR_OVERRIDE in buildworld as they most likely need # headers from default SUBDIR. Do SUBDIR_OVERRIDE includes last. ${_+_}cd ${.CURDIR}; ${WMAKE} SUBDIR_OVERRIDE= SHARED=symlinks \ MK_INCLUDES=yes includes .if !empty(SUBDIR_OVERRIDE) && make(buildworld) ${_+_}cd ${.CURDIR}; ${WMAKE} MK_INCLUDES=yes SHARED=symlinks includes .endif _libraries: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.2: building libraries" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; \ ${WMAKE} -DNO_FSCHG MK_HTML=no -DNO_LINT MK_MAN=no \ MK_PROFILE=no MK_TESTS=no MK_TESTS_SUPPORT=${MK_TESTS} libraries everything: .PHONY @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.3: building everything" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; _PARALLEL_SUBDIR_OK=1 ${WMAKE} all WMAKE_TGTS= .if !defined(WORLDFAST) WMAKE_TGTS+= _sanity_check _worldtmp _legacy .if empty(SUBDIR_OVERRIDE) WMAKE_TGTS+= _bootstrap-tools .endif WMAKE_TGTS+= _cleanobj .if !defined(NO_OBJWALK) WMAKE_TGTS+= _obj .endif WMAKE_TGTS+= _build-tools _cross-tools WMAKE_TGTS+= _build-metadata WMAKE_TGTS+= _includes .endif .if !defined(NO_LIBS) WMAKE_TGTS+= _libraries .endif WMAKE_TGTS+= everything .if defined(LIBCOMPAT) && empty(SUBDIR_OVERRIDE) WMAKE_TGTS+= build${libcompat} .endif buildworld: buildworld_prologue ${WMAKE_TGTS} buildworld_epilogue .PHONY .ORDER: buildworld_prologue ${WMAKE_TGTS} buildworld_epilogue buildworld_prologue: .PHONY @echo "--------------------------------------------------------------" @echo ">>> World build started on `LC_ALL=C date`" @echo "--------------------------------------------------------------" buildworld_epilogue: .PHONY @echo @echo "--------------------------------------------------------------" @echo ">>> World build completed on `LC_ALL=C date`" @echo "--------------------------------------------------------------" # # We need to have this as a target because the indirection between Makefile # and Makefile.inc1 causes the correct PATH to be used, rather than a # modification of the current environment's PATH. In addition, we need # to quote multiword values. # buildenvvars: .PHONY @echo ${WMAKEENV:Q} ${.MAKE.EXPORTED:@v@$v=\"${$v}\"@} .if ${.TARGETS:Mbuildenv} .if ${.MAKEFLAGS:M-j} .error The buildenv target is incompatible with -j .endif .endif BUILDENV_DIR?= ${.CURDIR} # # Note: make will report any errors the shell reports. This can # be odd if the last command in an interactive shell generates an # error or is terminated by SIGINT. These reported errors look bad, # but are harmless. Allowing them also allows BUIDLENV_SHELL to # be a complex command whose status will be returned to the caller. # Some scripts in tools rely on this behavior to report build errors. # buildenv: .PHONY @echo Entering world for ${TARGET_ARCH}:${TARGET} .if ${BUILDENV_SHELL:M*zsh*} @echo For ZSH you must run: export CPUTYPE=${TARGET_CPUTYPE} .endif @cd ${BUILDENV_DIR} && env ${WMAKEENV} BUILDENV=1 ${BUILDENV_SHELL} TOOLCHAIN_TGTS= ${WMAKE_TGTS:Neverything:Nbuild${libcompat}} toolchain: ${TOOLCHAIN_TGTS} .PHONY KERNEL_TOOLCHAIN_TGTS= ${TOOLCHAIN_TGTS:N_obj:N_cleanobj:N_includes:N_libraries} .if make(kernel-toolchain) .ORDER: ${KERNEL_TOOLCHAIN_TGTS} .endif kernel-toolchain: ${KERNEL_TOOLCHAIN_TGTS} .PHONY # # installcheck # # Checks to be sure system is ready for installworld/installkernel. # installcheck: _installcheck_world _installcheck_kernel .PHONY _installcheck_world: .PHONY @echo "--------------------------------------------------------------" @echo ">>> Install check world" @echo "--------------------------------------------------------------" _installcheck_kernel: .PHONY @echo "--------------------------------------------------------------" @echo ">>> Install check kernel" @echo "--------------------------------------------------------------" # # Require DESTDIR to be set if installing for a different architecture or # using the user/group database in the source tree. # .if ${TARGET_ARCH} != ${MACHINE_ARCH} || ${TARGET} != ${MACHINE} || \ defined(DB_FROM_SRC) .if !make(distributeworld) _installcheck_world: __installcheck_DESTDIR _installcheck_kernel: __installcheck_DESTDIR __installcheck_DESTDIR: .PHONY .if !defined(DESTDIR) || empty(DESTDIR) @echo "ERROR: Please set DESTDIR!"; \ false .endif .endif .endif .if !defined(DB_FROM_SRC) # # Check for missing UIDs/GIDs. # CHECK_UIDS= auditdistd CHECK_GIDS= audit CHECK_UIDS+= ntpd CHECK_GIDS+= ntpd CHECK_UIDS+= proxy CHECK_GIDS+= proxy authpf CHECK_UIDS+= smmsp CHECK_GIDS+= smmsp CHECK_UIDS+= unbound CHECK_GIDS+= unbound _installcheck_world: __installcheck_UGID __installcheck_UGID: .PHONY .for uid in ${CHECK_UIDS} @if ! `id -u ${uid} >/dev/null 2>&1`; then \ echo "ERROR: Required ${uid} user is missing, see /usr/src/UPDATING."; \ false; \ fi .endfor .for gid in ${CHECK_GIDS} @if ! `find / -prune -group ${gid} >/dev/null 2>&1`; then \ echo "ERROR: Required ${gid} group is missing, see /usr/src/UPDATING."; \ false; \ fi .endfor .endif # # If installing over the running system (DESTDIR is / or unset) and the install # includes rescue, try running rescue from the objdir as a sanity check. If # rescue is not functional (e.g., because it depends on a system call not # supported by the currently running kernel), abort the installation. # .if !make(distributeworld) && ${MK_RESCUE} != "no" && \ (empty(DESTDIR) || ${DESTDIR} == "/") && empty(BYPASS_INSTALLCHECK_SH) _installcheck_world: __installcheck_sh_check __installcheck_sh_check: .PHONY @if [ "`${OBJTOP}/rescue/rescue/rescue sh -c 'echo OK'`" != \ OK ]; then \ echo "rescue/sh check failed, installation aborted" >&2; \ false; \ fi .endif # # Required install tools to be saved in a scratch dir for safety. # .if ${MK_ZONEINFO} != "no" _zoneinfo= zic tzsetup .endif ITOOLS= [ awk cap_mkdb cat chflags chmod chown cmp cp \ date echo egrep find grep id install ${_install-info} \ ln make mkdir mtree mv pwd_mkdb \ rm sed services_mkdb sh sort strip sysctl test true uname wc ${_zoneinfo} \ ${LOCAL_ITOOLS} # Needed for share/man .if ${MK_MAN_UTILS} != "no" ITOOLS+=makewhatis .endif # # distributeworld # # Distributes everything compiled by a `buildworld'. # # installworld # # Installs everything compiled by a 'buildworld'. # # Non-base distributions produced by the base system EXTRA_DISTRIBUTIONS= .if defined(LIBCOMPAT) EXTRA_DISTRIBUTIONS+= lib${libcompat} .endif .if ${MK_TESTS} != "no" EXTRA_DISTRIBUTIONS+= tests .endif DEBUG_DISTRIBUTIONS= .if ${MK_DEBUG_FILES} != "no" DEBUG_DISTRIBUTIONS+= base ${EXTRA_DISTRIBUTIONS:S,tests,,} .endif MTREE_MAGIC?= mtree 2.0 distributeworld installworld stageworld: _installcheck_world .PHONY mkdir -p ${INSTALLTMP} progs=$$(for prog in ${ITOOLS}; do \ if progpath=`which $$prog`; then \ echo $$progpath; \ else \ echo "Required tool $$prog not found in PATH." >&2; \ exit 1; \ fi; \ done); \ libs=$$(ldd -f "%o %p\n" -f "%o %p\n" $$progs 2>/dev/null | sort -u | \ while read line; do \ set -- $$line; \ if [ "$$2 $$3" != "not found" ]; then \ echo $$2; \ else \ echo "Required library $$1 not found." >&2; \ exit 1; \ fi; \ done); \ cp $$libs $$progs ${INSTALLTMP} cp -R $${PATH_LOCALE:-"/usr/share/locale"} ${INSTALLTMP}/locale .if defined(NO_ROOT) -mkdir -p ${METALOG:H} echo "#${MTREE_MAGIC}" > ${METALOG} .endif .if make(distributeworld) .for dist in ${EXTRA_DISTRIBUTIONS} -mkdir ${DESTDIR}/${DISTDIR}/${dist} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.root.dist \ -p ${DESTDIR}/${DISTDIR}/${dist} >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/include >/dev/null .if ${MK_DEBUG_FILES} != "no" ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.debug.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/lib >/dev/null .endif .if defined(LIBCOMPAT) ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.lib${libcompat}.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr >/dev/null .if ${MK_DEBUG_FILES} != "no" ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.lib${libcompat}.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/lib/debug/usr >/dev/null .endif .endif .if ${MK_TESTS} != "no" && ${dist} == "tests" -mkdir -p ${DESTDIR}/${DISTDIR}/${dist}${TESTSBASE} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}${TESTSBASE} >/dev/null .if ${MK_DEBUG_FILES} != "no" ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/lib/debug/${TESTSBASE} >/dev/null .endif .endif .if defined(NO_ROOT) ${IMAKEENV} ${MTREE} -C -f ${.CURDIR}/etc/mtree/BSD.root.dist | \ sed -e 's#^\./#./${dist}/#' >> ${METALOG} ${IMAKEENV} ${MTREE} -C -f ${.CURDIR}/etc/mtree/BSD.usr.dist | \ sed -e 's#^\./#./${dist}/usr/#' >> ${METALOG} ${IMAKEENV} ${MTREE} -C -f ${.CURDIR}/etc/mtree/BSD.include.dist | \ sed -e 's#^\./#./${dist}/usr/include/#' >> ${METALOG} .if defined(LIBCOMPAT) ${IMAKEENV} ${MTREE} -C -f ${.CURDIR}/etc/mtree/BSD.lib${libcompat}.dist | \ sed -e 's#^\./#./${dist}/usr/#' >> ${METALOG} .endif .endif .endfor -mkdir ${DESTDIR}/${DISTDIR}/base ${_+_}cd ${.CURDIR}/etc; ${CROSSENV} PATH=${TMPPATH} ${MAKE} \ METALOG=${METALOG} ${IMAKE_INSTALL} ${IMAKE_MTREE} \ DISTBASE=/base DESTDIR=${DESTDIR}/${DISTDIR}/base \ LOCAL_MTREE=${LOCAL_MTREE:Q} distrib-dirs ${INSTALL_SYMLINK} ${INSTALLFLAGS} usr/src/sys ${INSTALL_DDIR}/base/sys .endif ${_+_}cd ${.CURDIR}; ${IMAKE} re${.TARGET:S/world$//}; \ ${IMAKEENV} rm -rf ${INSTALLTMP} .if make(distributeworld) .for dist in ${EXTRA_DISTRIBUTIONS} find ${DESTDIR}/${DISTDIR}/${dist} -mindepth 1 -type d -empty -delete .endfor .if defined(NO_ROOT) .for dist in base ${EXTRA_DISTRIBUTIONS} @# For each file that exists in this dist, print the corresponding @# line from the METALOG. This relies on the fact that @# a line containing only the filename will sort immediately before @# the relevant mtree line. cd ${DESTDIR}/${DISTDIR}; \ find ./${dist} | sort -u ${METALOG} - | \ awk 'BEGIN { print "#${MTREE_MAGIC}" } !/ type=/ { file = $$1 } / type=/ { if ($$1 == file) { sub(/^\.\/${dist}\//, "./"); print } }' > \ ${DESTDIR}/${DISTDIR}/${dist}.meta .endfor .for dist in ${DEBUG_DISTRIBUTIONS} @# For each file that exists in this dist, print the corresponding @# line from the METALOG. This relies on the fact that @# a line containing only the filename will sort immediately before @# the relevant mtree line. cd ${DESTDIR}/${DISTDIR}; \ find ./${dist}/usr/lib/debug | sort -u ${METALOG} - | \ awk 'BEGIN { print "#${MTREE_MAGIC}" } !/ type=/ { file = $$1 } / type=/ { if ($$1 == file) { sub(/^\.\/${dist}\//, "./"); print } }' > \ ${DESTDIR}/${DISTDIR}/${dist}.debug.meta .endfor .endif .endif packageworld: .PHONY .for dist in base ${EXTRA_DISTRIBUTIONS} .if defined(NO_ROOT) ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ tar cvf - --exclude usr/lib/debug \ @${DESTDIR}/${DISTDIR}/${dist}.meta | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}.txz .else ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ tar cvf - --exclude usr/lib/debug . | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}.txz .endif .endfor .for dist in ${DEBUG_DISTRIBUTIONS} . if defined(NO_ROOT) ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ tar cvf - @${DESTDIR}/${DISTDIR}/${dist}.debug.meta | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}-dbg.txz . else ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ tar cvLf - usr/lib/debug | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}-dbg.txz . endif .endfor _sysent_dirs= sys/kern _sysent_dirs+= sys/compat/freebsd32 _sysent_dirs+= sys/i386/ibcs2 _sysent_dirs+= sys/amd64/linux \ sys/amd64/linux32 \ sys/arm64/linux \ sys/i386/linux sysent: .PHONY .for _dir in ${_sysent_dirs} ${_+_}${MAKE} -C ${.CURDIR}/${_dir} sysent .endfor # # reinstall # # If you have a build server, you can NFS mount the source and obj directories # and do a 'make reinstall' on the *client* to install new binaries from the # most recent server build. # restage reinstall: .MAKE .PHONY @echo "--------------------------------------------------------------" @echo ">>> Making hierarchy" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 \ LOCAL_MTREE=${LOCAL_MTREE:Q} hierarchy .if make(restage) @echo "--------------------------------------------------------------" @echo ">>> Making distribution" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 \ LOCAL_MTREE=${LOCAL_MTREE:Q} distribution .endif @echo @echo "--------------------------------------------------------------" @echo ">>> Installing everything started on `LC_ALL=C date`" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 install .if defined(LIBCOMPAT) ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 install${libcompat} .endif @echo "--------------------------------------------------------------" @echo ">>> Installing everything completed on `LC_ALL=C date`" @echo "--------------------------------------------------------------" redistribute: .MAKE .PHONY @echo "--------------------------------------------------------------" @echo ">>> Distributing everything" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 distribute .if defined(LIBCOMPAT) ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 distribute${libcompat} \ DISTRIBUTION=lib${libcompat} .endif distrib-dirs distribution: .MAKE .PHONY ${_+_}cd ${.CURDIR}/etc; ${CROSSENV} PATH=${TMPPATH} ${MAKE} \ ${IMAKE_INSTALL} ${IMAKE_MTREE} METALOG=${METALOG} ${.TARGET} .if make(distribution) ${_+_}cd ${.CURDIR}; ${CROSSENV} PATH=${TMPPATH} \ ${MAKE} -f Makefile.inc1 ${IMAKE_INSTALL} \ METALOG=${METALOG} MK_TESTS=no installconfig .endif # # buildkernel and installkernel # # Which kernels to build and/or install is specified by setting # KERNCONF. If not defined a GENERIC kernel is built/installed. # Only the existing (depending TARGET) config files are used # for building kernels and only the first of these is designated # as the one being installed. # # Note that we have to use TARGET instead of TARGET_ARCH when # we're in kernel-land. Since only TARGET_ARCH is (expected) to # be set to cross-build, we have to make sure TARGET is set # properly. .if defined(KERNFAST) NO_KERNELCLEAN= t NO_KERNELCONFIG= t NO_KERNELOBJ= t # Shortcut for KERNCONF=Blah -DKERNFAST is now KERNFAST=Blah .if !defined(KERNCONF) && ${KERNFAST} != "1" KERNCONF=${KERNFAST} .endif .endif .if ${TARGET_ARCH} == "powerpc64" KERNCONF?= GENERIC64 .else KERNCONF?= GENERIC .endif INSTKERNNAME?= kernel KERNSRCDIR?= ${.CURDIR}/sys KRNLCONFDIR= ${KERNSRCDIR}/${TARGET}/conf KRNLOBJDIR= ${OBJTOP}${KERNSRCDIR:C,^${.CURDIR},,} KERNCONFDIR?= ${KRNLCONFDIR} BUILDKERNELS= INSTALLKERNEL= .if defined(NO_INSTALLKERNEL) # All of the BUILDKERNELS loops start at index 1. BUILDKERNELS+= dummy .endif .for _kernel in ${KERNCONF} .if !defined(_MKSHOWCONFIG) && exists(${KERNCONFDIR}/${_kernel}) BUILDKERNELS+= ${_kernel} .if empty(INSTALLKERNEL) && !defined(NO_INSTALLKERNEL) INSTALLKERNEL= ${_kernel} .endif .else .if make(buildkernel) .error Missing KERNCONF ${KERNCONFDIR}/${_kernel} .endif .endif .endfor _cleankernobj_fast_depend_hack: .PHONY # 20180320 remove stale generated assym.s after renaming to .inc in r331254 @if [ -e "${OBJTOP}/sys/${KERNCONF}/assym.s" ]; then \ echo "Removing stale generated assym files"; \ rm -f ${OBJTOP}/sys/${KERNCONF}/assym.* \ ${OBJTOP}/sys/${KERNCONF}/.depend.assym.*; \ fi ${WMAKE_TGTS:N_worldtmp:Nbuild${libcompat}} ${.ALLTARGETS:M_*:N_worldtmp}: .MAKE .PHONY # # buildkernel # # Builds all kernels defined by BUILDKERNELS. # buildkernel: .MAKE .PHONY .if empty(BUILDKERNELS:Ndummy) @echo "ERROR: Missing kernel configuration file(s) (${KERNCONF})."; \ false .endif @echo .for _kernel in ${BUILDKERNELS:Ndummy} @echo "--------------------------------------------------------------" @echo ">>> Kernel build for ${_kernel} started on `LC_ALL=C date`" @echo "--------------------------------------------------------------" @echo "===> ${_kernel}" mkdir -p ${KRNLOBJDIR} .if !defined(NO_KERNELCONFIG) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1: configuring the kernel" @echo "--------------------------------------------------------------" cd ${KRNLCONFDIR}; \ PATH=${TMPPATH} \ config ${CONFIGARGS} -d ${KRNLOBJDIR}/${_kernel} \ -I '${KERNCONFDIR}' '${KERNCONFDIR}/${_kernel}' .endif .if !defined(NO_CLEAN) && !defined(NO_KERNELCLEAN) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.1: cleaning up the object tree" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} ${CLEANDIR} .else ${_+_}cd ${.CURDIR}; ${WMAKE} _cleankernobj_fast_depend_hack .endif .if !defined(NO_KERNELOBJ) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.2: rebuilding the object tree" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} obj .endif @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.3: build tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${KTMAKE} kernel-tools @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3.1: building everything" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} all -DNO_MODULES_OBJ @echo "--------------------------------------------------------------" @echo ">>> Kernel build for ${_kernel} completed on `LC_ALL=C date`" @echo "--------------------------------------------------------------" .endfor NO_INSTALLEXTRAKERNELS?= yes # # installkernel, etc. # # Install the kernel defined by INSTALLKERNEL # installkernel installkernel.debug \ reinstallkernel reinstallkernel.debug: _installcheck_kernel .PHONY .if !defined(NO_INSTALLKERNEL) .if empty(INSTALLKERNEL) @echo "ERROR: No kernel \"${KERNCONF}\" to install."; \ false .endif @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${INSTALLKERNEL} on $$(LC_ALL=C date)" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${INSTALLKERNEL}; \ ${CROSSENV} PATH=${TMPPATH} \ ${MAKE} ${IMAKE_INSTALL} KERNEL=${INSTKERNNAME} ${.TARGET:S/kernel//} @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${INSTALLKERNEL} completed on $$(LC_ALL=C date)" @echo "--------------------------------------------------------------" .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${_kernel} $$(LC_ALL=C date)" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${_kernel}; \ ${CROSSENV} PATH=${TMPPATH} \ ${MAKE} ${IMAKE_INSTALL} KERNEL=${INSTKERNNAME}.${_kernel} ${.TARGET:S/kernel//} @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${_kernel} completed on $$(LC_ALL=C date)" @echo "--------------------------------------------------------------" .endfor .endif distributekernel distributekernel.debug: .PHONY .if !defined(NO_INSTALLKERNEL) .if empty(INSTALLKERNEL) @echo "ERROR: No kernel \"${KERNCONF}\" to install."; \ false .endif mkdir -p ${DESTDIR}/${DISTDIR} .if defined(NO_ROOT) @echo "#${MTREE_MAGIC}" > ${DESTDIR}/${DISTDIR}/kernel.premeta .endif ${_+_}cd ${KRNLOBJDIR}/${INSTALLKERNEL}; \ ${IMAKEENV} ${IMAKE_INSTALL:S/METALOG/kernel.premeta/} \ ${IMAKE_MTREE} PATH=${TMPPATH} ${MAKE} KERNEL=${INSTKERNNAME} \ DESTDIR=${INSTALL_DDIR}/kernel \ ${.TARGET:S/distributekernel/install/} .if defined(NO_ROOT) @sed -e 's|^./kernel|.|' ${DESTDIR}/${DISTDIR}/kernel.premeta > \ ${DESTDIR}/${DISTDIR}/kernel.meta .endif .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} .if defined(NO_ROOT) @echo "#${MTREE_MAGIC}" > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.premeta .endif ${_+_}cd ${KRNLOBJDIR}/${_kernel}; \ ${IMAKEENV} ${IMAKE_INSTALL:S/METALOG/kernel.${_kernel}.premeta/} \ ${IMAKE_MTREE} PATH=${TMPPATH} ${MAKE} \ KERNEL=${INSTKERNNAME}.${_kernel} \ DESTDIR=${INSTALL_DDIR}/kernel.${_kernel} \ ${.TARGET:S/distributekernel/install/} .if defined(NO_ROOT) @sed -e "s|^./kernel.${_kernel}|.|" \ ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.premeta > \ ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta .endif .endfor .endif packagekernel: .PHONY .if defined(NO_ROOT) .if !defined(NO_INSTALLKERNEL) cd ${DESTDIR}/${DISTDIR}/kernel; \ tar cvf - --exclude '*.debug' \ @${DESTDIR}/${DISTDIR}/kernel.meta | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.txz .endif .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel; \ tar cvf - --include '*/*/*.debug' \ @${DESTDIR}/${DISTDIR}/kernel.meta | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel-dbg.txz .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ tar cvf - --exclude '*.debug' \ @${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.${_kernel}.txz .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ tar cvf - --include '*/*/*.debug' \ @${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}-dbg.txz .endif .endfor .endif .else .if !defined(NO_INSTALLKERNEL) cd ${DESTDIR}/${DISTDIR}/kernel; \ tar cvf - --exclude '*.debug' . | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.txz .endif .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel; \ tar cvf - --include '*/*/*.debug' $$(eval find .) | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel-dbg.txz .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ tar cvf - --exclude '*.debug' . | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.${_kernel}.txz .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ tar cvf - --include '*/*/*.debug' $$(eval find .) | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}-dbg.txz .endif .endfor .endif .endif stagekernel: .PHONY ${_+_}${MAKE} -C ${.CURDIR} ${.MAKEFLAGS} distributekernel PORTSDIR?= /usr/ports WSTAGEDIR?= ${OBJTOP}/worldstage KSTAGEDIR?= ${OBJTOP}/kernelstage REPODIR?= ${OBJROOT}repo PKGSIGNKEY?= # empty .ORDER: stage-packages create-packages .ORDER: create-packages create-world-packages .ORDER: create-packages create-kernel-packages .ORDER: create-packages sign-packages _pkgbootstrap: .PHONY .if make(*package*) && !exists(${LOCALBASE}/sbin/pkg) @env ASSUME_ALWAYS_YES=YES pkg bootstrap .endif packages: .PHONY ${_+_}${MAKE} -C ${.CURDIR} PKG_VERSION=${PKG_VERSION} real-packages package-pkg: .PHONY rm -rf /tmp/ports.${TARGET} || : env ${WMAKEENV:Q} SRCDIR=${.CURDIR} PORTSDIR=${PORTSDIR} REVISION=${_REVISION} \ PKG_CMD=${PKG_CMD} PKG_VERSION=${PKG_VERSION} REPODIR=${REPODIR} \ WSTAGEDIR=${WSTAGEDIR} \ sh ${.CURDIR}/release/scripts/make-pkg-package.sh real-packages: stage-packages create-packages sign-packages .PHONY stage-packages-world: .PHONY @mkdir -p ${WSTAGEDIR} ${_+_}@cd ${.CURDIR}; \ ${MAKE} DESTDIR=${WSTAGEDIR} -DNO_ROOT stageworld stage-packages-kernel: .PHONY @mkdir -p ${KSTAGEDIR} ${_+_}@cd ${.CURDIR}; \ ${MAKE} DESTDIR=${KSTAGEDIR} -DNO_ROOT stagekernel stage-packages: .PHONY stage-packages-world stage-packages-kernel _repodir: .PHONY @mkdir -p ${REPODIR} create-packages-world: _pkgbootstrap _repodir .PHONY ${_+_}@cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 \ DESTDIR=${WSTAGEDIR} \ PKG_VERSION=${PKG_VERSION} create-world-packages create-packages-kernel: _pkgbootstrap _repodir .PHONY ${_+_}@cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 \ DESTDIR=${KSTAGEDIR} \ PKG_VERSION=${PKG_VERSION} DISTDIR=kernel \ create-kernel-packages create-packages: .PHONY create-packages-world create-packages-kernel create-world-packages: _pkgbootstrap .PHONY @rm -f ${WSTAGEDIR}/*.plist 2>/dev/null || : @cd ${WSTAGEDIR} ; \ env -i LC_COLLATE=C sort ${WSTAGEDIR}/${DISTDIR}/METALOG | \ awk -f ${SRCDIR}/release/scripts/mtree-to-plist.awk @for plist in ${WSTAGEDIR}/*.plist; do \ plist=$${plist##*/} ; \ pkgname=$${plist%.plist} ; \ echo "_PKGS+= $${pkgname}" ; \ done > ${WSTAGEDIR}/packages.mk ${_+_}@cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 create-world-packages-jobs \ .MAKE.JOB.PREFIX= .if make(create-world-packages-jobs) .include "${WSTAGEDIR}/packages.mk" .endif create-world-packages-jobs: .PHONY .for pkgname in ${_PKGS} create-world-packages-jobs: create-world-package-${pkgname} create-world-package-${pkgname}: .PHONY @sh ${SRCDIR}/release/packages/generate-ucl.sh -o ${pkgname} \ -s ${SRCDIR} -u ${WSTAGEDIR}/${pkgname}.ucl @awk -F\" ' \ /^name/ { printf("===> Creating %s-", $$2); next } \ /^version/ { print $$2; next } \ ' ${WSTAGEDIR}/${pkgname}.ucl @if [ "${pkgname}" == "runtime" ]; then \ sed -i '' -e "s/%VCS_REVISION%/${VCS_REVISION}/" ${WSTAGEDIR}/${pkgname}.ucl ; \ fi ${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh -o ALLOW_BASE_SHLIBS=yes \ create -M ${WSTAGEDIR}/${pkgname}.ucl \ -p ${WSTAGEDIR}/${pkgname}.plist \ -r ${WSTAGEDIR} \ -o ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh config ABI)/${PKG_VERSION} .endfor _default_flavor= -default .if make(*package*) && exists(${KSTAGEDIR}/kernel.meta) . if ${MK_DEBUG_FILES} != "no" _debug=-debug . endif create-kernel-packages: .PHONY . for flavor in "" ${_debug} create-kernel-packages: create-kernel-packages-flavor${flavor:C,^""$,${_default_flavor},} create-kernel-packages-flavor${flavor:C,^""$,${_default_flavor},}: _pkgbootstrap .PHONY @cd ${KSTAGEDIR}/${DISTDIR} ; \ env -i LC_COLLATE=C sort ${KSTAGEDIR}/kernel.meta | \ awk -f ${SRCDIR}/release/scripts/mtree-to-plist.awk \ -v kernel=yes -v _kernconf=${INSTALLKERNEL} ; \ sed -e "s/%VERSION%/${PKG_VERSION}/" \ -e "s/%PKGNAME%/kernel-${INSTALLKERNEL:tl}${flavor}/" \ -e "s/%KERNELDIR%/kernel/" \ -e "s/%COMMENT%/FreeBSD ${INSTALLKERNEL} kernel ${flavor}/" \ -e "s/%DESC%/FreeBSD ${INSTALLKERNEL} kernel ${flavor}/" \ -e "s/ %VCS_REVISION%/${VCS_REVISION}/" \ ${SRCDIR}/release/packages/kernel.ucl \ > ${KSTAGEDIR}/${DISTDIR}/kernel.${INSTALLKERNEL}${flavor}.ucl ; \ awk -F\" ' \ /name/ { printf("===> Creating %s-", $$2); next } \ /version/ {print $$2; next } ' \ ${KSTAGEDIR}/${DISTDIR}/kernel.${INSTALLKERNEL}${flavor}.ucl ; \ ${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh -o ALLOW_BASE_SHLIBS=yes \ create -M ${KSTAGEDIR}/${DISTDIR}/kernel.${INSTALLKERNEL}${flavor}.ucl \ -p ${KSTAGEDIR}/${DISTDIR}/kernel.${INSTALLKERNEL}${flavor}.plist \ -r ${KSTAGEDIR}/${DISTDIR} \ -o ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh config ABI)/${PKG_VERSION} . endfor .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" . for _kernel in ${BUILDKERNELS:[2..-1]} . if exists(${KSTAGEDIR}/kernel.${_kernel}.meta) . if ${MK_DEBUG_FILES} != "no" _debug=-debug . endif . for flavor in "" ${_debug} create-kernel-packages: create-kernel-packages-extra-flavor${flavor:C,^""$,${_default_flavor},}-${_kernel} create-kernel-packages-extra-flavor${flavor:C,^""$,${_default_flavor},}-${_kernel}: _pkgbootstrap .PHONY @cd ${KSTAGEDIR}/kernel.${_kernel} ; \ env -i LC_COLLATE=C sort ${KSTAGEDIR}/kernel.${_kernel}.meta | \ awk -f ${SRCDIR}/release/scripts/mtree-to-plist.awk \ -v kernel=yes -v _kernconf=${_kernel} ; \ sed -e "s/%VERSION%/${PKG_VERSION}/" \ -e "s/%PKGNAME%/kernel-${_kernel:tl}${flavor}/" \ -e "s/%KERNELDIR%/kernel.${_kernel}/" \ -e "s/%COMMENT%/FreeBSD ${_kernel} kernel ${flavor}/" \ -e "s/%DESC%/FreeBSD ${_kernel} kernel ${flavor}/" \ -e "s/ %VCS_REVISION%/${VCS_REVISION}/" \ ${SRCDIR}/release/packages/kernel.ucl \ > ${KSTAGEDIR}/kernel.${_kernel}/kernel.${_kernel}${flavor}.ucl ; \ awk -F\" ' \ /name/ { printf("===> Creating %s-", $$2); next } \ /version/ {print $$2; next } ' \ ${KSTAGEDIR}/kernel.${_kernel}/kernel.${_kernel}${flavor}.ucl ; \ ${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh -o ALLOW_BASE_SHLIBS=yes \ create -M ${KSTAGEDIR}/kernel.${_kernel}/kernel.${_kernel}${flavor}.ucl \ -p ${KSTAGEDIR}/kernel.${_kernel}/kernel.${_kernel}${flavor}.plist \ -r ${KSTAGEDIR}/kernel.${_kernel} \ -o ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh config ABI)/${PKG_VERSION} . endfor . endif . endfor .endif sign-packages: _pkgbootstrap .PHONY @[ -L "${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh config ABI)/latest" ] && \ unlink ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh config ABI)/latest ; \ ${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh repo \ -o ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh config ABI)/${PKG_VERSION} \ ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh config ABI)/${PKG_VERSION} \ ${PKGSIGNKEY} ; \ cd ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/bin/sh config ABI); \ ln -s ${PKG_VERSION} latest # # # checkworld # # Run test suite on installed world. # checkworld: .PHONY @if [ ! -x "${LOCALBASE}/bin/kyua" ]; then \ echo "You need kyua (devel/kyua) to run the test suite." | /usr/bin/fmt; \ exit 1; \ fi ${_+_}PATH="$$PATH:${LOCALBASE}/bin" kyua test -k ${TESTSBASE}/Kyuafile # # # doxygen # # Build the API documentation with doxygen # doxygen: .PHONY @if [ ! -x "${LOCALBASE}/bin/doxygen" ]; then \ echo "You need doxygen (devel/doxygen) to generate the API documentation of the kernel." | /usr/bin/fmt; \ exit 1; \ fi ${_+_}cd ${.CURDIR}/tools/kerneldoc/subsys; ${MAKE} obj all # # update # # Update the source tree(s), by running svn/svnup to update to the # latest copy. # update: .PHONY .if defined(SVN_UPDATE) @echo "--------------------------------------------------------------" @echo ">>> Updating ${.CURDIR} using Subversion" @echo "--------------------------------------------------------------" @(cd ${.CURDIR}; ${SVN_CMD} update ${SVNFLAGS}) .endif # # ------------------------------------------------------------------------ # # From here onwards are utility targets used by the 'make world' and # related targets. If your 'world' breaks, you may like to try to fix # the problem and manually run the following targets to attempt to # complete the build. Beware, this is *not* guaranteed to work, you # need to have a pretty good grip on the current state of the system # to attempt to manually finish it. If in doubt, 'make world' again. # # # legacy: Build compatibility shims for the next three targets. This is a # minimal set of tools and shims necessary to compensate for older systems # which don't have the APIs required by the targets built in bootstrap-tools, # build-tools or cross-tools. # # libnv and libl are both requirements for config(8), which is an unconditional # bootstrap-tool. _config_deps= lib/libnv usr.bin/lex/lib legacy: .PHONY .if ${BOOTSTRAPPING} < ${MINIMUM_SUPPORTED_OSREL} && ${BOOTSTRAPPING} != 0 @echo "ERROR: Source upgrades from versions prior to ${MINIMUM_SUPPORTED_REL} are not supported."; \ false .endif .for _tool in tools/build ${_config_deps} ${_+_}@${ECHODIR} "===> ${_tool} (obj,includes,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${WORLDTMP}/legacy includes; \ ${MAKE} DIRPRFX=${_tool}/ MK_INCLUDES=no all; \ ${MAKE} DIRPRFX=${_tool}/ MK_INCLUDES=no \ DESTDIR=${WORLDTMP}/legacy install .endfor # # bootstrap-tools: Build tools needed for compatibility. These are binaries that # are built to build other binaries in the system. However, the focus of these # binaries is usually quite narrow. Bootstrap tools use the host's compiler and # libraries, augmented by -legacy, in addition to the libraries built during # bootstrap-tools. # _bt= _bootstrap-tools # We want to run the build with only ${WORLDTMP} in $PATH to ensure we don't # accidentally run tools that are incompatible but happen to be in $PATH. # This is especially important when building on Linux/MacOS where many of the # programs used during the build accept different flags or generate different # output. On those platforms we only symlink the tools known to be compatible # (e.g. basic utilities such as mkdir) into ${WORLDTMP} and build all others # from the FreeBSD sources during the bootstrap-tools stage. # We want to build without the user's $PATH starting in the bootstrap-tools # phase so the tools used in that phase (ln, cp, etc) must have already been # linked to $WORLDTMP. The tools are listed in the _host_tools_to_symlink # variable in tools/build/Makefile and are linked during the legacy phase. # Since they could be Linux or MacOS binaries, too we must only use flags that # are portable across operating systems. # If BOOTSTRAP_ALL_TOOLS is set we will build all the required tools from the # current source tree. Otherwise we create a symlink to the version found in # $PATH during the bootstrap-tools stage. .if defined(BOOTSTRAP_ALL_TOOLS) # BOOTSTRAPPING will be set on the command line so we can't override it here. # Instead set BOOTSTRAPPING_OSRELDATE so that the value 0 is set ${BSARGS} BOOTSTRAPPING_OSRELDATE:= 0 .endif .if ${MK_GAMES} != "no" _strfile= usr.bin/fortune/strfile .endif .if ${MK_GCC} != "no" && ${MK_CXX} != "no" _gperf= gnu/usr.bin/gperf .endif .if ${MK_VT} != "no" _vtfontcvt= usr.bin/vtfontcvt .endif # If we are not building the bootstrap because BOOTSTRAPPING is sufficient # we symlink the host version to $WORLDTMP instead. By doing this we can also # detect when a bootstrap tool is being used without the required MK_FOO. # If you add a new bootstrap tool where we could also use the host version, # please ensure that you also add a .else case where you add the tool to the # _bootstrap_tools_links variable. .if ${BOOTSTRAPPING} < 1000033 _m4= usr.bin/m4 _lex= usr.bin/lex # Note: lex needs m4 to build but m4 also depends on lex. However, lex can be # bootstrapped so we build lex first. ${_bt}-usr.bin/m4: ${_bt}-lib/libopenbsd ${_bt}-usr.bin/yacc ${_bt}-${_lex} _bt_m4_depend=${_bt}-${_m4} _bt_lex_depend=${_bt}-${_lex} ${_bt_m4_depend} .else _bootstrap_tools_links+=m4 lex .endif # ELF Tool Chain libraries are needed for ELF tools and dtrace tools. # r296685 fix cross-endian objcopy # r310724 fixed PR 215350, a crash in libdwarf with objects built by GCC 6.2. # r334881 added libdwarf constants used by ctfconvert. # r338478 fixed a crash in objcopy for mips64el objects # r339083 libelf: correct mips64el test to use ELF header .if ${BOOTSTRAPPING} < 1200085 _elftoolchain_libs= lib/libelf lib/libdwarf ${_bt}-lib/libelf: ${_bt_m4_depend} ${_bt}-lib/libdwarf: ${_bt_m4_depend} .endif # r245440 mtree -N support added # r313404 requires sha384.h for libnetbsd, added to libmd in r292782 .if ${BOOTSTRAPPING} < 1100093 _nmtree= lib/libmd \ lib/libnetbsd \ usr.sbin/nmtree ${_bt}-lib/libnetbsd: ${_bt}-lib/libmd ${_bt}-usr.sbin/nmtree: ${_bt}-lib/libnetbsd .else _bootstrap_tools_links+=mtree .endif # r246097: log addition login.conf.db, passwd, pwd.db, and spwd.db with cat -l .if ${BOOTSTRAPPING} < 1000027 _cat= bin/cat .else _bootstrap_tools_links+=cat .endif # r277259 crunchide: Correct 64-bit section header offset # r281674 crunchide: always include both 32- and 64-bit ELF support .if ${BOOTSTRAPPING} < 1100078 _crunchide= usr.sbin/crunch/crunchide .else _bootstrap_tools_links+=crunchide .endif # r285986 crunchen: use STRIPBIN rather than STRIP # 1100113: Support MK_AUTO_OBJ # 1200006: META_MODE fixes .if ${BOOTSTRAPPING} < 1100078 || \ (${MK_AUTO_OBJ} == "yes" && ${BOOTSTRAPPING} < 1100114) || \ (${MK_META_MODE} == "yes" && ${BOOTSTRAPPING} < 1200006) _crunchgen= usr.sbin/crunch/crunchgen .else _bootstrap_tools_links+=crunchgen .endif # r296926 -P keymap search path, MFC to stable/10 in r298297 .if ${BOOTSTRAPPING} < 1003501 || \ (${BOOTSTRAPPING} >= 1100000 && ${BOOTSTRAPPING} < 1100103) _kbdcontrol= usr.sbin/kbdcontrol .else _bootstrap_tools_links+=kbdcontrol .endif _yacc= lib/liby \ usr.bin/yacc ${_bt}-usr.bin/yacc: ${_bt}-lib/liby .if ${MK_BSNMP} != "no" _gensnmptree= usr.sbin/bsnmpd/gensnmptree .endif .if ${MK_LOCALES} != "no" _localedef= usr.bin/localedef .endif # We need to build tblgen when we're building clang or lld, either as # bootstrap tools, or as the part of the normal build. .if ${MK_CLANG_BOOTSTRAP} != "no" || ${MK_CLANG} != "no" || \ ${MK_LLD_BOOTSTRAP} != "no" || ${MK_LLD} != "no" _clang_tblgen= \ lib/clang/libllvmminimal \ usr.bin/clang/llvm-tblgen \ usr.bin/clang/clang-tblgen ${_bt}-usr.bin/clang/clang-tblgen: ${_bt}-lib/clang/libllvmminimal ${_bt}-usr.bin/clang/llvm-tblgen: ${_bt}-lib/clang/libllvmminimal .endif # Default to building the GPL DTC, but build the BSDL one if users explicitly # request it. _dtc= usr.bin/dtc .if ${MK_GPL_DTC} != "no" _dtc= gnu/usr.bin/dtc .endif .if ${MK_LOCALES} != "no" _localedef= usr.bin/localedef .endif .if ${MK_KERBEROS} != "no" _kerberos5_bootstrap_tools= \ kerberos5/tools/make-roken \ kerberos5/lib/libroken \ kerberos5/lib/libvers \ kerberos5/tools/asn1_compile \ kerberos5/tools/slc \ usr.bin/compile_et .ORDER: ${_kerberos5_bootstrap_tools:C/^/${_bt}-/g} .for _tool in ${_kerberos5_bootstrap_tools} ${_bt}-${_tool}: ${_bt}-usr.bin/yacc ${_bt_lex_depend} .endfor .endif ${_bt}-usr.bin/mandoc: ${_bt}-lib/libopenbsd # The tools listed in _basic_bootstrap_tools will generally not be # bootstrapped unless BOOTSTRAP_ALL_TOOL is set. However, when building on a # Linux or MacOS host the host versions are incompatible so we need to build # them from the source tree. Usually the link name will be the same as the subdir, # but some directories such as grep or test install multiple binaries. In that # case we use the _basic_bootstrap_tools_multilink variable which is a list of # subdirectory and comma-separated list of files. _basic_bootstrap_tools_multilink=usr.bin/grep grep,egrep,fgrep _basic_bootstrap_tools_multilink+=bin/test test,[ # bootstrap tools needed by buildworld: _basic_bootstrap_tools=usr.bin/awk usr.bin/cut bin/expr usr.bin/gencat \ usr.bin/join usr.bin/mktemp bin/rmdir usr.bin/sed usr.bin/sort \ usr.bin/truncate usr.bin/tsort # elf2aout is required for sparc64 build _basic_bootstrap_tools+=usr.bin/elf2aout # file2c is required for building usr.sbin/config: _basic_bootstrap_tools+=usr.bin/file2c # uuencode/uudecode required for share/tabset _basic_bootstrap_tools+=usr.bin/uuencode usr.bin/uudecode # xargs is required by mkioctls _basic_bootstrap_tools+=usr.bin/xargs # cap_mkdb is required for share/termcap: _basic_bootstrap_tools+=usr.bin/cap_mkdb # ldd is required for installcheck (TODO: just always use /usr/bin/ldd instead?) _basic_bootstrap_tools+=usr.bin/ldd # services_mkdb/pwd_mkdb are required for installworld: _basic_bootstrap_tools+=usr.sbin/services_mkdb usr.sbin/pwd_mkdb # sysctl/chflags are required for installkernel: _basic_bootstrap_tools+=sbin/sysctl bin/chflags # mkfifo is used by sys/conf/newvers.sh _basic_bootstrap_tools+=usr.bin/mkfifo .if ${MK_AMD} != "no" # unifdef is only used by usr.sbin/amd/libamu/Makefile _basic_bootstrap_tools+=usr.bin/unifdef .endif .if ${MK_BOOT} != "no" _basic_bootstrap_tools+=bin/dd # xz/unxz is used by EFI _basic_bootstrap_tools_multilink+=usr.bin/xz xz,unxz # md5 is used by boot/beri (and possibly others) _basic_bootstrap_tools+=sbin/md5 .if defined(BOOTSTRAP_ALL_TOOLS) ${_bt}-sbin/md5: ${_bt}-lib/libmd .endif .endif .if ${MK_ZONEINFO} != "no" _basic_bootstrap_tools+=usr.sbin/zic usr.sbin/tzsetup .endif .if defined(BOOTSTRAP_ALL_TOOLS) _other_bootstrap_tools+=${_basic_bootstrap_tools} .for _subdir _links in ${_basic_bootstrap_tools_multilink} _other_bootstrap_tools+=${_subdir} .endfor ${_bt}-usr.bin/awk: ${_bt_lex_depend} ${_bt}-usr.bin/yacc ${_bt}-bin/expr: ${_bt_lex_depend} ${_bt}-usr.bin/yacc # If we are bootstrapping file2c, we have to build it before config: ${_bt}-usr.sbin/config: ${_bt}-usr.bin/file2c ${_bt_lex_depend} # Note: no symlink to make/bmake in the !BOOTSTRAP_ALL_TOOLS case here since # the links to make/bmake make links will have already have been created in the # `make legacy` step. Not adding a link to make is important on non-FreeBSD # since "make" will usually point to GNU make there. _other_bootstrap_tools+=usr.bin/bmake .else # All tools in _basic_bootstrap_tools have the same name as the subdirectory # so we can use :T to get the name of the symlinks that we need to create. _bootstrap_tools_links+=${_basic_bootstrap_tools:T} .for _subdir _links in ${_basic_bootstrap_tools_multilink} _bootstrap_tools_links+=${_links:S/,/ /g} .endfor .endif # defined(BOOTSTRAP_ALL_TOOLS) # Link the tools that we need for building but don't need to bootstrap because # the host version is known to be compatible into ${WORLDTMP}/legacy # We do this before building any of the bootstrap tools in case they depend on # the presence of any of the links (e.g. as m4/lex/awk) ${_bt}-links: .PHONY .for _tool in ${_bootstrap_tools_links} ${_bt}-link-${_tool}: .PHONY .MAKE @if [ ! -e "${WORLDTMP}/legacy/bin/${_tool}" ]; then \ source_path=`which ${_tool}`; \ if [ ! -e "$${source_path}" ] ; then \ echo "Cannot find host tool '${_tool}'"; false; \ fi; \ ln -sfnv "$${source_path}" "${WORLDTMP}/legacy/bin/${_tool}"; \ fi ${_bt}-links: ${_bt}-link-${_tool} .endfor bootstrap-tools: ${_bt}-links .PHONY # Please document (add comment) why something is in 'bootstrap-tools'. # Try to bound the building of the bootstrap-tool to just the # FreeBSD versions that need the tool built at this stage of the build. .for _tool in \ ${_clang_tblgen} \ ${_kerberos5_bootstrap_tools} \ ${_strfile} \ ${_gperf} \ ${_dtc} \ ${_cat} \ ${_kbdcontrol} \ ${_elftoolchain_libs} \ usr.bin/lorder \ lib/libopenbsd \ usr.bin/mandoc \ usr.bin/rpcgen \ ${_yacc} \ ${_m4} \ ${_lex} \ ${_other_bootstrap_tools} \ usr.bin/xinstall \ ${_gensnmptree} \ usr.sbin/config \ ${_crunchide} \ ${_crunchgen} \ ${_nmtree} \ ${_vtfontcvt} \ ${_localedef} ${_bt}-${_tool}: ${_bt}-links .PHONY .MAKE ${_+_}@${ECHODIR} "===> ${_tool} (obj,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ if [ "${_tool}" = "usr.bin/lex" ]; then \ ${MAKE} DIRPRFX=${_tool}/ bootstrap; \ fi; \ ${MAKE} DIRPRFX=${_tool}/ all; \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${WORLDTMP}/legacy install bootstrap-tools: ${_bt}-${_tool} .endfor # # build-tools: Build special purpose build tools # .if !defined(NO_SHARE) && ${MK_SYSCONS} != "no" _share= share/syscons/scrnmaps .endif .if ${MK_GCC} != "no" _gcc_tools= gnu/usr.bin/cc/cc_tools .endif .if ${MK_RESCUE} != "no" # rescue includes programs that have build-tools targets _rescue=rescue/rescue .endif .if ${MK_TCSH} != "no" _tcsh=bin/csh .endif .if ${MK_FILE} != "no" _libmagic=lib/libmagic .endif .if ${MK_PMC} != "no" && \ (${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386") _jevents=lib/libpmc/pmu-events .endif # kernel-toolchain skips _cleanobj, so handle cleaning up previous # build-tools directories if needed. .if !defined(NO_CLEAN) && make(kernel-toolchain) _bt_clean= ${CLEANDIR} .endif .for _tool in \ ${_tcsh} \ bin/sh \ ${LOCAL_TOOL_DIRS} \ ${_jevents} \ lib/ncurses/ncurses \ lib/ncurses/ncursesw \ ${_rescue} \ ${_share} \ usr.bin/awk \ ${_libmagic} \ usr.bin/mkesdb_static \ usr.bin/mkcsmapper_static \ usr.bin/vi/catalog \ ${_gcc_tools} build-tools_${_tool}: .PHONY ${_+_}@${ECHODIR} "===> ${_tool} (${_bt_clean:D${_bt_clean},}obj,build-tools)"; \ cd ${.CURDIR}/${_tool}; \ if [ -n "${_bt_clean}" ]; then ${MAKE} DIRPRFX=${_tool}/ ${_bt_clean}; fi; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ ${MAKE} DIRPRFX=${_tool}/ build-tools build-tools: build-tools_${_tool} .endfor # # kernel-tools: Build kernel-building tools # kernel-tools: .PHONY mkdir -p ${WORLDTMP}/usr ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${WORLDTMP}/usr >/dev/null # # cross-tools: All the tools needed to build the rest of the system after # we get done with the earlier stages. It is the last set of tools needed # to begin building the target binaries. # .if ${TARGET_ARCH} != ${MACHINE_ARCH} || ${BUILD_WITH_STRICT_TMPPATH} != 0 .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" _btxld= usr.sbin/btxld .endif .endif # Rebuild ctfconvert and ctfmerge to avoid difficult-to-diagnose failures # resulting from missing bug fixes or ELF Toolchain updates. .if ${MK_CDDL} != "no" _dtrace_tools= cddl/lib/libctf cddl/usr.bin/ctfconvert \ cddl/usr.bin/ctfmerge .endif # If we're given an XAS, don't build binutils. .if ${XAS:M/*} == "" .if ${MK_BINUTILS_BOOTSTRAP} != "no" _binutils= gnu/usr.bin/binutils .endif .if ${MK_ELFTOOLCHAIN_BOOTSTRAP} != "no" _elftctools= lib/libelftc \ lib/libpe \ usr.bin/objcopy \ usr.bin/nm \ usr.bin/size \ usr.bin/strings # These are not required by the build, but can be useful for developers who # cross-build on a FreeBSD 10 host: _elftctools+= usr.bin/addr2line .endif .elif ${TARGET_ARCH} != ${MACHINE_ARCH} && ${MK_ELFTOOLCHAIN_BOOTSTRAP} != "no" # If cross-building with an external binutils we still need to build strip for # the target (for at least crunchide). _elftctools= lib/libelftc \ lib/libpe \ usr.bin/objcopy .endif .if ${MK_CLANG_BOOTSTRAP} != "no" _clang= usr.bin/clang .endif .if ${MK_LLD_BOOTSTRAP} != "no" _lld= usr.bin/clang/lld .endif .if ${MK_CLANG_BOOTSTRAP} != "no" || ${MK_LLD_BOOTSTRAP} != "no" _clang_libs= lib/clang .endif .if ${MK_GCC_BOOTSTRAP} != "no" _gcc= gnu/usr.bin/cc .endif .if ${MK_USB} != "no" _usb_tools= stand/usb/tools .endif .if ${BUILD_WITH_STRICT_TMPPATH} != 0 || defined(BOOTSTRAP_ALL_TOOLS) _ar=usr.bin/ar .endif cross-tools: .MAKE .PHONY .for _tool in \ ${LOCAL_XTOOL_DIRS} \ ${_ar} \ ${_clang_libs} \ ${_clang} \ ${_lld} \ ${_binutils} \ ${_elftctools} \ ${_dtrace_tools} \ ${_gcc} \ ${_btxld} \ ${_usb_tools} ${_+_}@${ECHODIR} "===> ${_tool} (obj,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ ${MAKE} DIRPRFX=${_tool}/ all; \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${WORLDTMP} install .endfor # # native-xtools is the current target for qemu-user cross builds of ports # via poudriere and the imgact_binmisc kernel module. # This target merely builds a toolchan/sysroot, then builds the tools it wants # with the options it wants in a special MAKEOBJDIRPREFIX, using the toolchain # already built. It then installs the static tools to NXBDESTDIR for Poudriere # to pickup. # NXBOBJROOT= ${OBJROOT}${MACHINE}.${MACHINE_ARCH}/nxb/ NXBOBJTOP= ${NXBOBJROOT}${NXB_TARGET}.${NXB_TARGET_ARCH} NXTP?= /nxb-bin .if ${NXTP:N/*} .error NXTP variable should be an absolute path .endif NXBDESTDIR?= ${DESTDIR}${NXTP} # This is the list of tools to be built/installed as static and where # appropriate to build for the given TARGET.TARGET_ARCH. NXBDIRS+= \ bin/cat \ bin/chmod \ bin/cp \ ${_tcsh} \ bin/echo \ bin/expr \ bin/hostname \ bin/ln \ bin/ls \ bin/mkdir \ bin/mv \ bin/ps \ bin/realpath \ bin/rm \ bin/rmdir \ bin/sh \ bin/sleep \ sbin/md5 \ sbin/sysctl \ usr.bin/addr2line \ usr.bin/ar \ usr.bin/awk \ usr.bin/basename \ usr.bin/bmake \ usr.bin/bzip2 \ usr.bin/cmp \ usr.bin/diff \ usr.bin/dirname \ usr.bin/objcopy \ usr.bin/env \ usr.bin/fetch \ usr.bin/find \ usr.bin/grep \ usr.bin/gzip \ usr.bin/id \ usr.bin/lex \ usr.bin/limits \ usr.bin/lorder \ usr.bin/mandoc \ usr.bin/mktemp \ usr.bin/mt \ usr.bin/nm \ usr.bin/patch \ usr.bin/readelf \ usr.bin/sed \ usr.bin/size \ usr.bin/sort \ usr.bin/strings \ usr.bin/tar \ usr.bin/touch \ usr.bin/tr \ usr.bin/true \ usr.bin/uniq \ usr.bin/unzip \ usr.bin/xargs \ usr.bin/xinstall \ usr.bin/xz \ usr.bin/yacc \ usr.sbin/chown SUBDIR_DEPEND_usr.bin/clang= lib/clang .if ${MK_CLANG} != "no" NXBDIRS+= lib/clang NXBDIRS+= usr.bin/clang .endif .if ${MK_GCC} != "no" NXBDIRS+= gnu/usr.bin/cc .endif .if ${MK_BINUTILS} != "no" NXBDIRS+= gnu/usr.bin/binutils .endif # XXX: native-xtools passes along ${NXBDIRS} in SUBDIR_OVERRIDE that needs # to be evaluated after NXBDIRS is set. .if make(install) && !empty(SUBDIR_OVERRIDE) SUBDIR= ${SUBDIR_OVERRIDE} .endif NXBMAKEARGS+= \ OBJTOP=${NXBOBJTOP:Q} \ OBJROOT=${NXBOBJROOT:Q} \ MAKEOBJDIRPREFIX= \ -DNO_SHARED \ -DNO_CPU_CFLAGS \ -DNO_PIC \ SSP_CFLAGS= \ MK_CLANG_EXTRAS=no \ MK_CLANG_FULL=no \ MK_CTF=no \ MK_DEBUG_FILES=no \ MK_GDB=no \ MK_HTML=no \ MK_LLDB=no \ MK_MAN=no \ MK_MAN_UTILS=yes \ MK_OFED=no \ MK_OPENSSH=no \ MK_PROFILE=no \ MK_RETPOLINE=no \ MK_SENDMAIL=no \ MK_SVNLITE=no \ MK_TESTS=no \ MK_WARNS=no \ MK_ZFS=no .if make(native-xtools*) && \ (!defined(NXB_TARGET) || !defined(NXB_TARGET_ARCH)) .error Missing NXB_TARGET / NXB_TARGET_ARCH .endif # For 'toolchain' we want to produce native binaries that themselves generate # native binaries. NXBTMAKE= ${NXBMAKEENV} ${MAKE} ${NXBMAKEARGS:N-DNO_PIC:N-DNO_SHARED} \ TARGET=${MACHINE} TARGET_ARCH=${MACHINE_ARCH} # For 'everything' we want to produce native binaries (hence -target to # be MACHINE) that themselves generate TARGET.TARGET_ARCH binaries. # TARGET/TARGET_ARCH are still passed along from user. # # Use the toolchain we create as an external toolchain. .if ${USING_SYSTEM_COMPILER} == "yes" || ${XCC:N${CCACHE_BIN}:M/*} NXBMAKE+= XCC="${XCC}" \ XCXX="${XCXX}" \ XCPP="${XCPP}" .else NXBMAKE+= XCC="${NXBOBJTOP}/tmp/usr/bin/cc" \ XCXX="${NXBOBJTOP}/tmp/usr/bin/c++" \ XCPP="${NXBOBJTOP}/tmp/usr/bin/cpp" .endif NXBMAKE+= ${NXBMAKEENV} ${MAKE} -f Makefile.inc1 ${NXBMAKEARGS} \ TARGET=${NXB_TARGET} TARGET_ARCH=${NXB_TARGET_ARCH} \ TARGET_TRIPLE=${MACHINE_TRIPLE:Q} # NXBDIRS is improperly based on MACHINE rather than NXB_TARGET. Need to # invoke a sub-make to reevaluate MK_GCC, etc, for NXBDIRS. NXBMAKE+= SUBDIR_OVERRIDE='$${NXBDIRS:M*}' # Need to avoid the -isystem logic when using clang as an external toolchain # even if the TARGET being built for wants GCC. NXBMAKE+= WANT_COMPILER_TYPE='$${X_COMPILER_TYPE}' native-xtools: .PHONY ${_+_}cd ${.CURDIR}; ${NXBTMAKE} _cleanobj MK_GCC=yes # Build the bootstrap/host/cross tools that produce native binaries # Pass along MK_GCC=yes to ensure GCC-needed build tools are built. # We don't quite know what the NXB_TARGET wants so just build it. ${_+_}cd ${.CURDIR}; ${NXBTMAKE} kernel-toolchain MK_GCC=yes # Populate includes/libraries sysroot that produce native binaries. # This is split out from 'toolchain' above mostly so that target LLVM # libraries have a proper LLVM_DEFAULT_TARGET_TRIPLE without # polluting the cross-compiler build. The LLVM/GCC libs are skipped # here to avoid the problem but are kept in 'toolchain' so that # needed build tools are built. ${_+_}cd ${.CURDIR}; ${NXBTMAKE} _includes MK_CLANG=no MK_GCC=no ${_+_}cd ${.CURDIR}; ${NXBTMAKE} _libraries MK_CLANG=no MK_GCC=no # Clean out improper TARGET=MACHINE files ${_+_}cd ${.CURDIR}/gnu/usr.bin/cc/cc_tools; ${NXBTMAKE} cleandir .if !defined(NO_OBJWALK) ${_+_}cd ${.CURDIR}; ${NXBMAKE} _obj .endif ${_+_}cd ${.CURDIR}; ${NXBMAKE} everything @echo ">> native-xtools done. Use 'make native-xtools-install' to install to a given DESTDIR" native-xtools-install: .PHONY mkdir -p ${NXBDESTDIR}/bin ${NXBDESTDIR}/sbin ${NXBDESTDIR}/usr ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${NXBDESTDIR}/usr >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${NXBDESTDIR}/usr/include >/dev/null ${_+_}cd ${.CURDIR}; ${NXBMAKE} \ DESTDIR=${NXBDESTDIR} \ -DNO_ROOT \ install # # hierarchy - ensure that all the needed directories are present # hierarchy hier: .MAKE .PHONY ${_+_}cd ${.CURDIR}/etc; ${HMAKE} distrib-dirs # # libraries - build all libraries, and install them under ${DESTDIR}. # # The list of libraries with dependents (${_prebuild_libs}) and their # interdependencies (__L) are built automatically by the # ${.CURDIR}/tools/make_libdeps.sh script. # libraries: .MAKE .PHONY ${_+_}cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 _prereq_libs; \ ${MAKE} -f Makefile.inc1 _startup_libs; \ ${MAKE} -f Makefile.inc1 _prebuild_libs; \ ${MAKE} -f Makefile.inc1 _generic_libs # # static libgcc.a prerequisite for shared libc # _prereq_libs= lib/libcompiler_rt .if ${MK_SSP} != "no" _prereq_libs+= gnu/lib/libssp/libssp_nonshared .endif # These dependencies are not automatically generated: # # gnu/lib/csu, gnu/lib/libgcc, lib/csu and lib/libc must be built before # all shared libraries for ELF. # _startup_libs= lib/csu .if ${MK_BSD_CRTBEGIN} == "no" _startup_libs+= gnu/lib/csu .endif _startup_libs+= lib/libcompiler_rt _startup_libs+= lib/libc _startup_libs+= lib/libc_nonshared .if ${MK_LIBCPLUSPLUS} != "no" _startup_libs+= lib/libcxxrt .endif .if ${MK_LLVM_LIBUNWIND} != "no" _prereq_libs+= lib/libgcc_eh lib/libgcc_s _startup_libs+= lib/libgcc_eh lib/libgcc_s lib/libgcc_s__L: lib/libc__L lib/libgcc_s__L: lib/libc_nonshared__L .if ${MK_LIBCPLUSPLUS} != "no" lib/libcxxrt__L: lib/libgcc_s__L .endif .else # MK_LLVM_LIBUNWIND == no _prereq_libs+= gnu/lib/libgcc _startup_libs+= gnu/lib/libgcc gnu/lib/libgcc__L: lib/libc__L gnu/lib/libgcc__L: lib/libc_nonshared__L .if ${MK_LIBCPLUSPLUS} != "no" lib/libcxxrt__L: gnu/lib/libgcc__L .endif .endif _prebuild_libs= ${_kerberos5_lib_libasn1} \ ${_kerberos5_lib_libhdb} \ ${_kerberos5_lib_libheimbase} \ ${_kerberos5_lib_libheimntlm} \ ${_libsqlite3} \ ${_kerberos5_lib_libheimipcc} \ ${_kerberos5_lib_libhx509} ${_kerberos5_lib_libkrb5} \ ${_kerberos5_lib_libroken} \ ${_kerberos5_lib_libwind} \ lib/libbz2 ${_libcom_err} lib/libcrypt \ lib/libelf lib/libexpat \ lib/libfigpar \ ${_lib_libgssapi} \ lib/libkiconv lib/libkvm lib/liblzma lib/libmd lib/libnv \ ${_lib_casper} \ lib/ncurses/ncurses lib/ncurses/ncursesw \ lib/libopie lib/libpam/libpam ${_lib_libthr} \ ${_lib_libradius} lib/libsbuf lib/libtacplus \ lib/libgeom \ ${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \ ${_cddl_lib_libuutil} \ ${_cddl_lib_libavl} \ ${_cddl_lib_libzfs_core} ${_cddl_lib_libzfs} \ ${_cddl_lib_libctf} \ lib/libufs \ lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \ ${_secure_lib_libcrypto} ${_secure_lib_libssl} \ ${_lib_libldns} ${_secure_lib_libssh} .if ${MK_GNUCXX} != "no" _prebuild_libs+= gnu/lib/libstdc++ gnu/lib/libsupc++ gnu/lib/libstdc++__L: lib/msun__L gnu/lib/libsupc++__L: gnu/lib/libstdc++__L .endif .if ${MK_DIALOG} != "no" _prebuild_libs+= gnu/lib/libdialog gnu/lib/libdialog__L: lib/msun__L lib/ncurses/ncursesw__L .endif .if ${MK_LIBCPLUSPLUS} != "no" _prebuild_libs+= lib/libc++ .endif lib/libgeom__L: lib/libexpat__L lib/libkvm__L: lib/libelf__L .if ${MK_LIBTHR} != "no" _lib_libthr= lib/libthr .endif .if ${MK_RADIUS_SUPPORT} != "no" _lib_libradius= lib/libradius .endif .if ${MK_OFED} != "no" _prebuild_libs+= \ lib/ofed/libibverbs \ lib/ofed/libibmad \ lib/ofed/libibumad \ lib/ofed/complib \ lib/ofed/libmlx5 lib/ofed/libibmad__L: lib/ofed/libibumad__L lib/ofed/complib__L: lib/libthr__L lib/ofed/libmlx5__L: lib/ofed/libibverbs__L lib/libthr__L .endif .if ${MK_CASPER} != "no" _lib_casper= lib/libcasper .endif lib/libpjdlog__L: lib/libutil__L lib/libcasper__L: lib/libnv__L lib/liblzma__L: lib/libthr__L _generic_libs= ${_cddl_lib} gnu/lib ${_kerberos5_lib} lib ${_secure_lib} usr.bin/lex/lib .if ${MK_IPFILTER} != "no" _generic_libs+= sbin/ipf/libipf .endif .for _DIR in ${LOCAL_LIB_DIRS} .if ${_DIR} == ".WAIT" || (empty(_generic_libs:M${_DIR}) && exists(${.CURDIR}/${_DIR}/Makefile)) _generic_libs+= ${_DIR} .endif .endfor lib/libopie__L lib/libtacplus__L: lib/libmd__L .if ${MK_CDDL} != "no" _cddl_lib_libumem= cddl/lib/libumem _cddl_lib_libnvpair= cddl/lib/libnvpair _cddl_lib_libavl= cddl/lib/libavl _cddl_lib_libuutil= cddl/lib/libuutil .if ${MK_ZFS} != "no" _cddl_lib_libzfs_core= cddl/lib/libzfs_core _cddl_lib_libzfs= cddl/lib/libzfs cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L cddl/lib/libzfs__L: cddl/lib/libzfs_core__L lib/msun__L lib/libutil__L cddl/lib/libzfs__L: lib/libthr__L lib/libmd__L lib/libz__L cddl/lib/libumem__L cddl/lib/libzfs__L: cddl/lib/libuutil__L cddl/lib/libavl__L lib/libgeom__L lib/libbe__L: cddl/lib/libzfs__L .endif _cddl_lib_libctf= cddl/lib/libctf _cddl_lib= cddl/lib cddl/lib/libctf__L: lib/libz__L .endif # cddl/lib/libdtrace requires lib/libproc and lib/librtld_db; it's only built # on select architectures though (see cddl/lib/Makefile) .if ${MACHINE_CPUARCH} != "sparc64" _prebuild_libs+= lib/libprocstat lib/libproc lib/librtld_db lib/libprocstat__L: lib/libelf__L lib/libkvm__L lib/libutil__L lib/libproc__L: lib/libprocstat__L lib/librtld_db__L: lib/libprocstat__L .endif .if ${MK_CRYPT} != "no" .if ${MK_OPENSSL} != "no" _secure_lib_libcrypto= secure/lib/libcrypto _secure_lib_libssl= secure/lib/libssl lib/libradius__L secure/lib/libssl__L: secure/lib/libcrypto__L secure/lib/libcrypto__L: lib/libthr__L .if ${MK_LDNS} != "no" _lib_libldns= lib/libldns lib/libldns__L: secure/lib/libssl__L .endif .if ${MK_OPENSSH} != "no" _secure_lib_libssh= secure/lib/libssh secure/lib/libssh__L: lib/libz__L secure/lib/libcrypto__L lib/libcrypt__L .if ${MK_LDNS} != "no" secure/lib/libssh__L: lib/libldns__L .endif .if ${MK_GSSAPI} != "no" && ${MK_KERBEROS_SUPPORT} != "no" secure/lib/libssh__L: lib/libgssapi__L kerberos5/lib/libkrb5__L \ kerberos5/lib/libhx509__L kerberos5/lib/libasn1__L lib/libcom_err__L \ lib/libmd__L kerberos5/lib/libroken__L .endif .endif .endif _secure_lib= secure/lib .endif .if ${MK_KERBEROS} != "no" kerberos5/lib/libasn1__L: lib/libcom_err__L kerberos5/lib/libroken__L kerberos5/lib/libhdb__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ kerberos5/lib/libkrb5__L kerberos5/lib/libroken__L \ kerberos5/lib/libwind__L lib/libsqlite3__L kerberos5/lib/libheimntlm__L: secure/lib/libcrypto__L kerberos5/lib/libkrb5__L \ kerberos5/lib/libroken__L lib/libcom_err__L kerberos5/lib/libhx509__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ secure/lib/libcrypto__L kerberos5/lib/libroken__L kerberos5/lib/libwind__L kerberos5/lib/libkrb5__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ lib/libcrypt__L secure/lib/libcrypto__L kerberos5/lib/libhx509__L \ kerberos5/lib/libroken__L kerberos5/lib/libwind__L \ kerberos5/lib/libheimbase__L kerberos5/lib/libheimipcc__L kerberos5/lib/libroken__L: lib/libcrypt__L kerberos5/lib/libwind__L: kerberos5/lib/libroken__L lib/libcom_err__L kerberos5/lib/libheimbase__L: lib/libthr__L kerberos5/lib/libheimipcc__L: kerberos5/lib/libroken__L kerberos5/lib/libheimbase__L lib/libthr__L .endif lib/libsqlite3__L: lib/libthr__L .if ${MK_GSSAPI} != "no" _lib_libgssapi= lib/libgssapi .endif .if ${MK_KERBEROS} != "no" _kerberos5_lib= kerberos5/lib _kerberos5_lib_libasn1= kerberos5/lib/libasn1 _kerberos5_lib_libhdb= kerberos5/lib/libhdb _kerberos5_lib_libheimbase= kerberos5/lib/libheimbase _kerberos5_lib_libkrb5= kerberos5/lib/libkrb5 _kerberos5_lib_libhx509= kerberos5/lib/libhx509 _kerberos5_lib_libroken= kerberos5/lib/libroken _kerberos5_lib_libheimntlm= kerberos5/lib/libheimntlm _libsqlite3= lib/libsqlite3 _kerberos5_lib_libheimipcc= kerberos5/lib/libheimipcc _kerberos5_lib_libwind= kerberos5/lib/libwind _libcom_err= lib/libcom_err .endif .if ${MK_NIS} != "no" _lib_libypclnt= lib/libypclnt .endif .if ${MK_OPENSSL} == "no" lib/libradius__L: lib/libmd__L .endif lib/libproc__L: \ ${_cddl_lib_libctf:D${_cddl_lib_libctf}__L} lib/libelf__L lib/librtld_db__L lib/libutil__L .if ${MK_CXX} != "no" .if ${MK_LIBCPLUSPLUS} != "no" lib/libproc__L: lib/libcxxrt__L .else # This implies MK_GNUCXX != "no"; see lib/libproc lib/libproc__L: gnu/lib/libsupc++__L .endif .endif .for _lib in ${_prereq_libs} ${_lib}__PL: .PHONY .MAKE .if !defined(_MKSHOWCONFIG) && exists(${.CURDIR}/${_lib}) ${_+_}@${ECHODIR} "===> ${_lib} (obj,all,install)"; \ cd ${.CURDIR}/${_lib}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ obj; fi; \ ${MAKE} MK_TESTS=no MK_PROFILE=no -DNO_PIC \ DIRPRFX=${_lib}/ all; \ ${MAKE} MK_TESTS=no MK_PROFILE=no -DNO_PIC \ DIRPRFX=${_lib}/ install .endif .endfor .for _lib in ${_startup_libs} ${_prebuild_libs} ${_generic_libs} ${_lib}__L: .PHONY .MAKE .if !defined(_MKSHOWCONFIG) && exists(${.CURDIR}/${_lib}) ${_+_}@${ECHODIR} "===> ${_lib} (obj,all,install)"; \ cd ${.CURDIR}/${_lib}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ obj; fi; \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ all; \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ install .endif .endfor _prereq_libs: ${_prereq_libs:S/$/__PL/} _startup_libs: ${_startup_libs:S/$/__L/} _prebuild_libs: ${_prebuild_libs:S/$/__L/} _generic_libs: ${_generic_libs:S/$/__L/} # Enable SUBDIR_PARALLEL when not calling 'make all', unless called from # 'everything' with _PARALLEL_SUBDIR_OK set. This is because it is unlikely # that running 'make all' from the top-level, especially with a SUBDIR_OVERRIDE # or LOCAL_DIRS set, will have a reliable build if SUBDIRs are built in # parallel. This is safe for the world stage of buildworld though since it has # already built libraries in a proper order and installed includes into # WORLDTMP. Special handling is done for SUBDIR ordering for 'install*' to # avoid trashing a system if it crashes mid-install. .if !make(all) || defined(_PARALLEL_SUBDIR_OK) SUBDIR_PARALLEL= .endif .include .if make(check-old) || make(check-old-dirs) || \ make(check-old-files) || make(check-old-libs) || \ make(delete-old) || make(delete-old-dirs) || \ make(delete-old-files) || make(delete-old-libs) # # check for / delete old files section # .include "ObsoleteFiles.inc" OLD_LIBS_MESSAGE="Please be sure no application still uses those libraries, \ else you can not start such an application. Consult UPDATING for more \ information regarding how to cope with the removal/revision bump of a \ specific library." .if !defined(BATCH_DELETE_OLD_FILES) RM_I=-i .else RM_I=-v .endif delete-old-files: .PHONY @echo ">>> Removing old files (only deletes safe to delete libs)" # Ask for every old file if the user really wants to remove it. # It's annoying, but better safe than sorry. # NB: We cannot pass the list of OLD_FILES as a parameter because the # argument list will get too long. Using .for/.endfor make "loops" will make # the Makefile parser segfault. @exec 3<&0; \ cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_FILES -V "OLD_FILES:Musr/share/*.gz:R" | xargs -n1 | sort | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ chflags noschg "${DESTDIR}/$${file}" 2>/dev/null || true; \ rm ${RM_I} "${DESTDIR}/$${file}" <&3; \ fi; \ for ext in debug symbols; do \ if ! [ -e "${DESTDIR}/$${file}" ] && [ -f \ "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ rm ${RM_I} "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" \ <&3; \ fi; \ done; \ done # Remove catpages without corresponding manpages. @exec 3<&0; \ find ${DESTDIR}/usr/share/man/cat* ! -type d 2>/dev/null | sort | \ sed -ep -e's:${DESTDIR}/usr/share/man/cat:${DESTDIR}/usr/share/man/man:' | \ while read catpage; do \ read manpage; \ if [ ! -e "$${manpage}" ]; then \ rm ${RM_I} $${catpage} <&3; \ fi; \ done @echo ">>> Old files removed" check-old-files: .PHONY @echo ">>> Checking for old files" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_FILES -V "OLD_FILES:Musr/share/*.gz:R" | xargs -n1 | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ echo "${DESTDIR}/$${file}"; \ fi; \ for ext in debug symbols; do \ if [ -f "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}"; \ fi; \ done; \ done | sort # Check for catpages without corresponding manpages. @find ${DESTDIR}/usr/share/man/cat* ! -type d 2>/dev/null | \ sed -ep -e's:${DESTDIR}/usr/share/man/cat:${DESTDIR}/usr/share/man/man:' | \ while read catpage; do \ read manpage; \ if [ ! -e "$${manpage}" ]; then \ echo $${catpage}; \ fi; \ done | sort delete-old-libs: .PHONY @echo ">>> Removing old libraries" @echo "${OLD_LIBS_MESSAGE}" | fmt @exec 3<&0; \ cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_LIBS | xargs -n1 | sort | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ chflags noschg "${DESTDIR}/$${file}" 2>/dev/null || true; \ rm ${RM_I} "${DESTDIR}/$${file}" <&3; \ fi; \ for ext in debug symbols; do \ if ! [ -e "${DESTDIR}/$${file}" ] && [ -f \ "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ rm ${RM_I} "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" \ <&3; \ fi; \ done; \ done @echo ">>> Old libraries removed" check-old-libs: .PHONY @echo ">>> Checking for old libraries" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_LIBS | xargs -n1 | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ echo "${DESTDIR}/$${file}"; \ fi; \ for ext in debug symbols; do \ if [ -f "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}"; \ fi; \ done; \ done | sort delete-old-dirs: .PHONY @echo ">>> Removing old directories" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_DIRS | xargs -n1 | sort -r | \ while read dir; do \ if [ -d "${DESTDIR}/$${dir}" ]; then \ rmdir -v "${DESTDIR}/$${dir}" || true; \ elif [ -L "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ if [ -d "${DESTDIR}${DEBUGDIR}/$${dir}" ]; then \ rmdir -v "${DESTDIR}${DEBUGDIR}/$${dir}" || true; \ elif [ -L "${DESTDIR}${DEBUGDIR}/$${dir}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ done @echo ">>> Old directories removed" check-old-dirs: .PHONY @echo ">>> Checking for old directories" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_DIRS | xargs -n1 | sort -r | \ while read dir; do \ if [ -d "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir}"; \ elif [ -L "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ if [ -d "${DESTDIR}${DEBUGDIR}/$${dir}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${dir}"; \ elif [ -L "${DESTDIR}${DEBUGDIR}/$${dir}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ done delete-old: delete-old-files delete-old-dirs .PHONY @echo "To remove old libraries run '${MAKE_CMD} delete-old-libs'." check-old: check-old-files check-old-libs check-old-dirs .PHONY @echo "To remove old files and directories run '${MAKE_CMD} delete-old'." @echo "To remove old libraries run '${MAKE_CMD} delete-old-libs'." .endif # # showconfig - show build configuration. # showconfig: .PHONY @(${MAKE} -n -f ${.CURDIR}/sys/conf/kern.opts.mk -V dummy -dg1 UPDATE_DEPENDFILE=no NO_OBJ=yes; \ ${MAKE} -n -f ${.CURDIR}/share/mk/src.opts.mk -V dummy -dg1 UPDATE_DEPENDFILE=no NO_OBJ=yes) 2>&1 | grep ^MK_ | sort -u .if !empty(KRNLOBJDIR) && !empty(KERNCONF) DTBOUTPUTPATH= ${KRNLOBJDIR}/${KERNCONF}/ .if !defined(FDT_DTS_FILE) || empty(FDT_DTS_FILE) .if !defined(_MKSHOWCONFIG) && exists(${KERNCONFDIR}/${KERNCONF}) FDT_DTS_FILE!= awk 'BEGIN {FS="="} /^makeoptions[[:space:]]+FDT_DTS_FILE/ {print $$2}' \ '${KERNCONFDIR}/${KERNCONF}' ; echo .endif .endif .endif .if !defined(DTBOUTPUTPATH) || !exists(${DTBOUTPUTPATH}) DTBOUTPUTPATH= ${.CURDIR} .endif # # Build 'standalone' Device Tree Blob # builddtb: .PHONY @PATH=${TMPPATH} MACHINE=${TARGET} \ ${.CURDIR}/sys/tools/fdt/make_dtb.sh ${.CURDIR}/sys \ "${FDT_DTS_FILE}" ${DTBOUTPUTPATH} ############### # cleanworld # In the following, the first 'rm' in a series will usually remove all # files and directories. If it does not, then there are probably some # files with file flags set, so this unsets them and tries the 'rm' a # second time. There are situations where this target will be cleaning # some directories via more than one method, but that duplication is # needed to correctly handle all the possible situations. Removing all # files without file flags set in the first 'rm' instance saves time, # because 'chflags' will need to operate on fewer files afterwards. # # It is expected that BW_CANONICALOBJDIR == the CANONICALOBJDIR as would be # created by bsd.obj.mk, except that we don't want to .include that file # in this makefile. We don't do a cleandir walk if MK_AUTO_OBJ is yes # since it is not possible for files to land in the wrong place. # .if make(cleanworld) BW_CANONICALOBJDIR:=${OBJTOP}/ .elif make(cleanuniverse) BW_CANONICALOBJDIR:=${OBJROOT} .if ${MK_UNIFIED_OBJDIR} == "no" .error ${.TARGETS} only supported with WITH_UNIFIED_OBJDIR enabled. .endif .endif cleanworld cleanuniverse: .PHONY .if !empty(BW_CANONICALOBJDIR) && exists(${BW_CANONICALOBJDIR}) && \ ${.CURDIR:tA} != ${BW_CANONICALOBJDIR:tA} -rm -rf ${BW_CANONICALOBJDIR}* -chflags -R 0 ${BW_CANONICALOBJDIR} rm -rf ${BW_CANONICALOBJDIR}* .endif .if make(cleanworld) && ${MK_AUTO_OBJ} == "no" && \ (empty(BW_CANONICALOBJDIR) || ${.CURDIR:tA} == ${BW_CANONICALOBJDIR:tA}) .if ${.CURDIR} == ${.OBJDIR} || ${.CURDIR}/obj == ${.OBJDIR} # To be safe in this case, fall back to a 'make cleandir' ${_+_}@cd ${.CURDIR}; ${MAKE} cleandir .endif .endif .if ${TARGET} == ${MACHINE} && ${TARGET_ARCH} == ${MACHINE_ARCH} XDEV_CPUTYPE?=${CPUTYPE} .else XDEV_CPUTYPE?=${TARGET_CPUTYPE} .endif NOFUN=-DNO_FSCHG MK_HTML=no -DNO_LINT \ MK_MAN=no MK_NLS=no MK_PROFILE=no \ MK_KERBEROS=no MK_RESCUE=no MK_TESTS=no MK_WARNS=no \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ CPUTYPE=${XDEV_CPUTYPE} XDDIR=${TARGET_ARCH}-freebsd XDTP?=/usr/${XDDIR} .if ${XDTP:N/*} .error XDTP variable should be an absolute path .endif CDBOBJROOT= ${OBJROOT}${MACHINE}.${MACHINE_ARCH}/xdev/ CDBOBJTOP= ${CDBOBJROOT}${XDDIR} CDBENV= \ INSTALL="sh ${.CURDIR}/tools/install.sh" CDENV= ${CDBENV} \ TOOLS_PREFIX=${XDTP} CDMAKEARGS= \ OBJTOP=${CDBOBJTOP:Q} \ OBJROOT=${CDBOBJROOT:Q} CD2MAKEARGS= ${CDMAKEARGS} .if ${WANT_COMPILER_TYPE} == gcc || \ (defined(X_COMPILER_TYPE) && ${X_COMPILER_TYPE} == gcc) # GCC requires -isystem and -L when using a cross-compiler. --sysroot # won't set header path and -L is used to ensure the base library path # is added before the port PREFIX library path. CD2CFLAGS+= -isystem ${XDDESTDIR}/usr/include -L${XDDESTDIR}/usr/lib # GCC requires -B to find /usr/lib/crti.o when using a cross-compiler # combined with --sysroot. CD2CFLAGS+= -B${XDDESTDIR}/usr/lib # Force using libc++ for external GCC. .if defined(X_COMPILER_TYPE) && \ ${X_COMPILER_TYPE} == gcc && ${X_COMPILER_VERSION} >= 40800 CD2CXXFLAGS+= -isystem ${XDDESTDIR}/usr/include/c++/v1 -std=c++11 \ -nostdinc++ .endif .endif CD2CFLAGS+= --sysroot=${XDDESTDIR}/ CD2ENV=${CDENV} CC="${CC} ${CD2CFLAGS}" CXX="${CXX} ${CD2CXXFLAGS} ${CD2CFLAGS}" \ CPP="${CPP} ${CD2CFLAGS}" \ MACHINE=${TARGET} MACHINE_ARCH=${TARGET_ARCH} CDTMP= ${OBJTOP}/${XDDIR}/tmp CDMAKE=${CDENV} PATH=${CDTMP}/usr/bin:${PATH} ${MAKE} ${CDMAKEARGS} ${NOFUN} CD2MAKE=${CD2ENV} PATH=${CDTMP}/usr/bin:${XDDESTDIR}/usr/bin:${PATH} \ ${MAKE} ${CD2MAKEARGS} ${NOFUN} .if ${MK_META_MODE} != "no" # Don't rebuild build-tools targets during normal build. CD2MAKE+= BUILD_TOOLS_META=.NOMETA .endif XDDESTDIR=${DESTDIR}${XDTP} .ORDER: xdev-build xdev-install xdev-links xdev: xdev-build xdev-install .PHONY .ORDER: _xb-worldtmp _xb-bootstrap-tools _xb-build-tools _xb-cross-tools xdev-build: _xb-worldtmp _xb-bootstrap-tools _xb-build-tools _xb-cross-tools .PHONY _xb-worldtmp: .PHONY mkdir -p ${CDTMP}/usr ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${CDTMP}/usr >/dev/null _xb-bootstrap-tools: .PHONY .for _tool in \ ${_clang_tblgen} \ ${_gperf} \ ${_yacc} ${_+_}@${ECHODIR} "===> ${_tool} (obj,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${CDMAKE} DIRPRFX=${_tool}/ obj; fi; \ ${CDMAKE} DIRPRFX=${_tool}/ all; \ ${CDMAKE} DIRPRFX=${_tool}/ DESTDIR=${CDTMP} install .endfor _xb-build-tools: .PHONY ${_+_}@cd ${.CURDIR}; \ ${CDBENV} ${MAKE} ${CDMAKEARGS} -f Makefile.inc1 ${NOFUN} build-tools XDEVDIRS= \ ${_clang_libs} \ ${_lld} \ ${_binutils} \ ${_elftctools} \ usr.bin/ar \ ${_clang} \ ${_gcc} _xb-cross-tools: .PHONY .for _tool in ${XDEVDIRS} ${_+_}@${ECHODIR} "===> xdev ${_tool} (obj,all)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${CDMAKE} DIRPRFX=${_tool}/ obj; fi; \ ${CDMAKE} DIRPRFX=${_tool}/ all .endfor _xi-mtree: .PHONY ${_+_}@${ECHODIR} "mtree populating ${XDDESTDIR}" mkdir -p ${XDDESTDIR} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.root.dist \ -p ${XDDESTDIR} >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${XDDESTDIR}/usr >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${XDDESTDIR}/usr/include >/dev/null .if defined(LIBCOMPAT) ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.lib${libcompat}.dist \ -p ${XDDESTDIR}/usr >/dev/null .endif .if ${MK_TESTS} != "no" mkdir -p ${XDDESTDIR}${TESTSBASE} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${XDDESTDIR}${TESTSBASE} >/dev/null .endif .ORDER: xdev-build _xi-mtree _xi-cross-tools _xi-includes _xi-libraries xdev-install: xdev-build _xi-mtree _xi-cross-tools _xi-includes _xi-libraries .PHONY _xi-cross-tools: .PHONY @echo "_xi-cross-tools" .for _tool in ${XDEVDIRS} ${_+_}@${ECHODIR} "===> xdev ${_tool} (install)"; \ cd ${.CURDIR}/${_tool}; \ ${CDMAKE} DIRPRFX=${_tool}/ install DESTDIR=${XDDESTDIR} .endfor _xi-includes: .PHONY .if !defined(NO_OBJWALK) ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 _obj \ DESTDIR=${XDDESTDIR} .endif ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 includes \ DESTDIR=${XDDESTDIR} _xi-libraries: .PHONY ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 libraries \ DESTDIR=${XDDESTDIR} xdev-links: .PHONY ${_+_}cd ${XDDESTDIR}/usr/bin; \ mkdir -p ../../../../usr/bin; \ for i in *; do \ ln -sf ../../${XDTP}/usr/bin/$$i \ ../../../../usr/bin/${XDDIR}-$$i; \ ln -sf ../../${XDTP}/usr/bin/$$i \ ../../../../usr/bin/${XDDIR}${_REVISION}-$$i; \ done Index: projects/clang700-import/lib/libc/sys/getfh.2 =================================================================== --- projects/clang700-import/lib/libc/sys/getfh.2 (revision 341812) +++ projects/clang700-import/lib/libc/sys/getfh.2 (revision 341813) @@ -1,237 +1,237 @@ .\" Copyright (c) 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" Copyright (c) 2018 Gandi .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)getfh.2 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd December 7, 2018 +.Dd December 11, 2018 .Dt GETFH 2 .Os .Sh NAME .Nm getfh , .Nm lgetfh , .Nm getfhat .Nd get file handle .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In sys/param.h .In sys/mount.h .Ft int .Fn getfh "const char *path" "fhandle_t *fhp" .Ft int .Fn lgetfh "const char *path" "fhandle_t *fhp" .Ft int .Fn getfhat "int fd" "const char *path" "fhandle_t *fhp" "int flag" .Sh DESCRIPTION The .Fn getfh system call returns a file handle for the specified file or directory in the file handle pointed to by .Fa fhp . .Pp The .Fn lgetfh system call is like .Fn getfh except in the case where the named file is a symbolic link, in which case .Fn lgetfh returns information about the link, while .Fn getfh returns information about the file the link references. .Pp The .Fn getfhat system call is equivalent to .Fn getfh and .Fn lgetfh except when the .Fa path -specifies a relative or NULL path, or the +specifies a relative path, or the .Dv AT_BENEATH flag is provided. For .Fn getfhat -and relative or NULL +and relative .Fa path , the status is retrieved from a file relative to the directory associated with the file descriptor .Fa fd instead of the current working directory. For .Dv AT_BENEATH and absolute .Fa path , the status is retrieved from a file specified by the .Fa path , but additional permission checks are performed, see below. .Pp The values for the .Fa flag are constructed by a bitwise-inclusive OR of flags from this list, defined in .In fcntl.h : .Bl -tag -width indent .It Dv AT_SYMLINK_NOFOLLOW If .Fa path names a symbolic link, the status of the symbolic link is returned. .It Dv AT_BENEATH Only stat files and directories below the topping directory. See the description of the .Dv O_BENEATH flag in the .Xr open 2 manual page. .El .Pp If .Fn getfhat is passed the special value .Dv AT_FDCWD in the .Fa fd parameter, the current working directory is used and the behavior is identical to a call to .Fn getfth or .Fn lgetfh respectively, depending on whether or not the .Dv AT_SYMLINK_NOFOLLOW bit is set in .Fa flag . .Pp When .Fn getfhat is called with an absolute .Fa path without the .Dv AT_BENEATH flag, it ignores the .Fa fd argument. When .Dv AT_BENEATH is specified with an absolute .Fa path , a directory passed by the .Fa fd argument is used as the topping point for the resolution. These system calls are restricted to the superuser. .Sh RETURN VALUES .Rv -std .Sh ERRORS The .Fn getfh and .Fn lgetfh system calls fail if one or more of the following are true: .Bl -tag -width Er .It Bq Er ENOTDIR A component of the path prefix of .Fa path is not a directory. .It Bq Er ENAMETOOLONG The length of a component of .Fa path exceeds 255 characters, or the length of .Fa path exceeds 1023 characters. .It Bq Er ENOENT The file referred to by .Fa path does not exist. .It Bq Er EACCES Search permission is denied for a component of the path prefix of .Fa path . .It Bq Er ELOOP Too many symbolic links were encountered in translating .Fa path . .It Bq Er EFAULT The .Fa fhp argument points to an invalid address. .It Bq Er EFAULT The .Fa path argument points to an invalid address. .It Bq Er EIO An .Tn I/O error occurred while reading from or writing to the file system. .It Bq Er ESTALE The file handle .Fa fhp is no longer valid. .El .Pp In addition to the errors returned by .Fn getfh , and .Fn lgetfh , the .Fn getfhat system call may fail if: .Bl -tag -width Er .It Bq Er EBADF The .Fa path argument does not specify an absolute path and the .Fa fd argument, is neither .Dv AT_FDCWD nor a valid file descriptor open for searching. .It Bq Er EINVAL The value of the .Fa flag argument is not valid. .It Bq Er ENOTDIR The .Fa path argument is not an absolute path and .Fa fd is neither .Dv AT_FDCWD nor a file descriptor associated with a directory. .Sh SEE ALSO .Xr fhopen 2 , .Xr open 2 , .Xr stat 2 .Sh HISTORY The .Fn getfh system call first appeared in .Bx 4.4 . Index: projects/clang700-import/libexec/rc/rc.subr =================================================================== --- projects/clang700-import/libexec/rc/rc.subr (revision 341812) +++ projects/clang700-import/libexec/rc/rc.subr (revision 341813) @@ -1,2165 +1,2177 @@ # $NetBSD: rc.subr,v 1.67 2006/10/07 11:25:15 elad Exp $ # $FreeBSD$ # # Copyright (c) 1997-2004 The NetBSD Foundation, Inc. # All rights reserved. # # This code is derived from software contributed to The NetBSD Foundation # by Luke Mewburn. # # 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # rc.subr # functions used by various rc scripts # : ${RC_PID:=$$}; export RC_PID # # Operating System dependent/independent variables # if [ -n "${_rc_subr_loaded}" ]; then return fi _rc_subr_loaded="YES" SYSCTL="/sbin/sysctl" SYSCTL_N="${SYSCTL} -n" SYSCTL_W="${SYSCTL}" PROTECT="/usr/bin/protect" ID="/usr/bin/id" IDCMD="if [ -x $ID ]; then $ID -un; fi" PS="/bin/ps -ww" JID=0 # # functions # --------- # list_vars pattern -# List vars matching pattern. +# List variables matching glob pattern. # list_vars() { - set | { while read LINE; do - var="${LINE%%=*}" - case "$var" in - "$LINE"|*[!a-zA-Z0-9_]*) continue ;; - $1) echo $var + # Localize 'set' option below. + local - + local IFS=$'\n' line varname + + # Disable path expansion in unquoted 'for' parameters below. + set -o noglob + + for line in $(set); do + varname="${line%%=*}" + + case "$varname" in + "$line"|*[!a-zA-Z0-9_]*) + continue + ;; + $1) + echo $varname + ;; esac - done; } + done } # set_rcvar [var] [defval] [desc] # # Echo or define a rc.conf(5) variable name. Global variable # $rcvars is used. # # If no argument is specified, echo "${name}_enable". # # If only a var is specified, echo "${var}_enable". # # If var and defval are specified, the ${var} is defined as # rc.conf(5) variable and the default value is ${defvar}. An # optional argument $desc can also be specified to add a # description for that. # set_rcvar() { local _var case $# in 0) echo ${name}_enable ;; 1) echo ${1}_enable ;; *) debug "set_rcvar: \$$1=$2 is added" \ " as a rc.conf(5) variable." _var=$1 rcvars="${rcvars# } $_var" eval ${_var}_defval=\"$2\" shift 2 eval ${_var}_desc=\"$*\" ;; esac } # set_rcvar_obsolete oldvar [newvar] [msg] # Define obsolete variable. # Global variable $rcvars_obsolete is used. # set_rcvar_obsolete() { local _var _var=$1 debug "set_rcvar_obsolete: \$$1(old) -> \$$2(new) is defined" rcvars_obsolete="${rcvars_obsolete# } $1" eval ${1}_newvar=\"$2\" shift 2 eval ${_var}_obsolete_msg=\"$*\" } # # force_depend script [rcvar] # Force a service to start. Intended for use by services # to resolve dependency issues. # $1 - filename of script, in /etc/rc.d, to run # $2 - name of the script's rcvar (minus the _enable) # force_depend() { local _depend _dep_rcvar _depend="$1" _dep_rcvar="${2:-$1}_enable" [ -n "$rc_fast" ] && ! checkyesno always_force_depends && checkyesno $_dep_rcvar && return 0 /etc/rc.d/${_depend} forcestatus >/dev/null 2>&1 && return 0 info "${name} depends on ${_depend}, which will be forced to start." if ! /etc/rc.d/${_depend} forcestart; then warn "Unable to force ${_depend}. It may already be running." return 1 fi } # # checkyesno var # Test $1 variable, and warn if not set to YES or NO. # Return 0 if it's "yes" (et al), nonzero otherwise. # checkyesno() { eval _value=\$${1} debug "checkyesno: $1 is set to $_value." case $_value in # "yes", "true", "on", or "1" [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0 ;; # "no", "false", "off", or "0" [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1 ;; *) warn "\$${1} is not set properly - see rc.conf(5)." return 1 ;; esac } # # reverse_list list # print the list in reverse order # reverse_list() { _revlist= for _revfile; do _revlist="$_revfile $_revlist" done echo $_revlist } # stop_boot always # If booting directly to multiuser or $always is enabled, # send SIGTERM to the parent (/etc/rc) to abort the boot. # Otherwise just exit. # stop_boot() { local always case $1 in # "yes", "true", "on", or "1" [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) always=true ;; *) always=false ;; esac if [ "$autoboot" = yes -o "$always" = true ]; then echo "ERROR: ABORTING BOOT (sending SIGTERM to parent)!" kill -TERM ${RC_PID} fi exit 1 } # # mount_critical_filesystems type # Go through the list of critical filesystems as provided in # the rc.conf(5) variable $critical_filesystems_${type}, checking # each one to see if it is mounted, and if it is not, mounting it. # mount_critical_filesystems() { eval _fslist=\$critical_filesystems_${1} for _fs in $_fslist; do mount | ( _ismounted=false while read what _on on _type type; do if [ $on = $_fs ]; then _ismounted=true fi done if $_ismounted; then : else mount $_fs >/dev/null 2>&1 fi ) done } # # check_pidfile pidfile procname [interpreter] # Parses the first line of pidfile for a PID, and ensures # that the process is running and matches procname. # Prints the matching PID upon success, nothing otherwise. # interpreter is optional; see _find_processes() for details. # check_pidfile() { _pidfile=$1 _procname=$2 _interpreter=$3 if [ -z "$_pidfile" -o -z "$_procname" ]; then err 3 'USAGE: check_pidfile pidfile procname [interpreter]' fi if [ ! -f $_pidfile ]; then debug "pid file ($_pidfile): not readable." return fi read _pid _junk < $_pidfile if [ -z "$_pid" ]; then debug "pid file ($_pidfile): no pid in file." return fi _find_processes $_procname ${_interpreter:-.} '-p '"$_pid" } # # check_process procname [interpreter] # Ensures that a process (or processes) named procname is running. # Prints a list of matching PIDs. # interpreter is optional; see _find_processes() for details. # check_process() { _procname=$1 _interpreter=$2 if [ -z "$_procname" ]; then err 3 'USAGE: check_process procname [interpreter]' fi _find_processes $_procname ${_interpreter:-.} '-ax' } # # _find_processes procname interpreter psargs # Search for procname in the output of ps generated by psargs. # Prints the PIDs of any matching processes, space separated. # # If interpreter == ".", check the following variations of procname # against the first word of each command: # procname # `basename procname` # `basename procname` + ":" # "(" + `basename procname` + ")" # "[" + `basename procname` + "]" # # If interpreter != ".", read the first line of procname, remove the # leading #!, normalise whitespace, append procname, and attempt to # match that against each command, either as is, or with extra words # at the end. As an alternative, to deal with interpreted daemons # using perl, the basename of the interpreter plus a colon is also # tried as the prefix to procname. # _find_processes() { if [ $# -ne 3 ]; then err 3 'USAGE: _find_processes procname interpreter psargs' fi _procname=$1 _interpreter=$2 _psargs=$3 _pref= if [ $_interpreter != "." ]; then # an interpreted script _script="${_chroot}${_chroot:+/}$_procname" if [ -r "$_script" ]; then read _interp < $_script # read interpreter name case "$_interp" in \#!*) _interp=${_interp#\#!} # strip #! set -- $_interp case $1 in */bin/env) shift # drop env to get real name ;; esac if [ $_interpreter != $1 ]; then warn "\$command_interpreter $_interpreter != $1" fi ;; *) warn "no shebang line in $_script" set -- $_interpreter ;; esac else warn "cannot read shebang line from $_script" set -- $_interpreter fi _interp="$* $_procname" # cleanup spaces, add _procname _interpbn=${1##*/} _fp_args='_argv' _fp_match='case "$_argv" in ${_interp}|"${_interp} "*|"[${_interpbn}]"|"${_interpbn}: ${_procname}"*)' else # a normal daemon _procnamebn=${_procname##*/} _fp_args='_arg0 _argv' _fp_match='case "$_arg0" in $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})"|"[${_procnamebn}]")' fi _proccheck="\ $PS 2>/dev/null -o pid= -o jid= -o command= $_psargs"' | while read _npid _jid '"$_fp_args"'; do '"$_fp_match"' if [ "$JID" -eq "$_jid" ]; then echo -n "$_pref$_npid"; _pref=" "; fi ;; esac done' # debug "in _find_processes: proccheck is ($_proccheck)." eval $_proccheck } # sort_lite [-b] [-n] [-k POS] [-t SEP] # A lite version of sort(1) (supporting a few options) that can be used # before the real sort(1) is available (e.g., in scripts that run prior # to mountcritremote). Requires only shell built-in functionality. # sort_lite() { local funcname=sort_lite local sort_sep="$IFS" sort_ignore_leading_space= local sort_field=0 sort_strict_fields= sort_numeric= local nitems=0 skip_leading=0 trim= local OPTIND flag while getopts bnk:t: flag; do case "$flag" in b) sort_ignore_leading_space=1 ;; n) sort_numeric=1 sort_ignore_leading_space=1 ;; k) sort_field="${OPTARG%%,*}" ;; # only up to first comma # NB: Unlike sort(1) only one POS allowed t) sort_sep="$OPTARG" if [ ${#sort_sep} -gt 1 ]; then echo "$funcname: multi-character tab \`$sort_sep'" >&2 return 1 fi sort_strict_fields=1 ;; \?) return 1 ;; esac done shift $(( $OPTIND - 1 )) # Create transformation pattern to trim leading text if desired case "$sort_field" in ""|[!0-9]*|*[!0-9.]*) echo "$funcname: invalid sort field \`$sort_field'" >&2 return 1 ;; *.*) skip_leading=${sort_field#*.} sort_field=${sort_field%%.*} while [ ${skip_leading:-0} -gt 1 ] 2> /dev/null; do trim="$trim?" skip_leading=$(( $skip_leading - 1 )) done esac # Copy input to series of local numbered variables # NB: IFS of NULL preserves leading whitespace local LINE while IFS= read -r LINE || [ "$LINE" ]; do nitems=$(( $nitems + 1 )) local src_$nitems="$LINE" done # # Sort numbered locals using insertion sort # local curitem curitem_orig curitem_mod curitem_haskey local dest dest_orig dest_mod dest_haskey local d gt n local i=1 while [ $i -le $nitems ]; do curitem_haskey=1 # Assume sort field (-k POS) exists eval curitem=\"\$src_$i\" curitem_mod="$curitem" # for modified comparison curitem_orig="$curitem" # for original comparison # Trim leading whitespace if desired if [ "$sort_ignore_leading_space" ]; then while case "$curitem_orig" in [$IFS]*) : ;; *) false; esac do curitem_orig="${curitem_orig#?}" done curitem_mod="$curitem_orig" fi # Shift modified comparison value if sort field (-k POS) is > 1 n=$sort_field while [ $n -gt 1 ]; do case "$curitem_mod" in *[$sort_sep]*) # Cut text up-to (and incl.) first separator curitem_mod="${curitem_mod#*[$sort_sep]}" # Skip NULLs unless strict field splitting [ "$sort_strict_fields" ] || [ "${curitem_mod%%[$sort_sep]*}" ] || [ $n -eq 2 ] || continue ;; *) # Asked for a field that doesn't exist curitem_haskey= break esac n=$(( $n - 1 )) done # Trim trailing words if sort field >= 1 [ $sort_field -ge 1 -a "$sort_numeric" ] && curitem_mod="${curitem_mod%%[$sort_sep]*}" # Apply optional trim (-k POS.TRIM) to cut leading characters curitem_mod="${curitem_mod#$trim}" # Determine the type of modified comparison to use initially # NB: Prefer numerical if requested but fallback to standard case "$curitem_mod" in ""|[!0-9]*) # NULL or begins with non-number gt=">" [ "$sort_numeric" ] && curitem_mod=0 ;; *) if [ "$sort_numeric" ]; then gt="-gt" curitem_mod="${curitem_mod%%[!0-9]*}" # NB: trailing non-digits removed # otherwise numeric comparison fails else gt=">" fi esac # If first time through, short-circuit below position-search if [ $i -le 1 ]; then d=0 else d=1 fi # # Find appropriate element position # while [ $d -gt 0 ] do dest_haskey=$curitem_haskey eval dest=\"\$dest_$d\" dest_mod="$dest" # for modified comparison dest_orig="$dest" # for original comparison # Trim leading whitespace if desired if [ "$sort_ignore_leading_space" ]; then while case "$dest_orig" in [$IFS]*) : ;; *) false; esac do dest_orig="${dest_orig#?}" done dest_mod="$dest_orig" fi # Shift modified value if sort field (-k POS) is > 1 n=$sort_field while [ $n -gt 1 ]; do case "$dest_mod" in *[$sort_sep]*) # Cut text up-to (and incl.) 1st sep dest_mod="${dest_mod#*[$sort_sep]}" # Skip NULLs unless strict fields [ "$sort_strict_fields" ] || [ "${dest_mod%%[$sort_sep]*}" ] || [ $n -eq 2 ] || continue ;; *) # Asked for a field that doesn't exist dest_haskey= break esac n=$(( $n - 1 )) done # Trim trailing words if sort field >= 1 [ $sort_field -ge 1 -a "$sort_numeric" ] && dest_mod="${dest_mod%%[$sort_sep]*}" # Apply optional trim (-k POS.TRIM), cut leading chars dest_mod="${dest_mod#$trim}" # Determine type of modified comparison to use # NB: Prefer numerical if requested, fallback to std case "$dest_mod" in ""|[!0-9]*) # NULL or begins with non-number gt=">" [ "$sort_numeric" ] && dest_mod=0 ;; *) if [ "$sort_numeric" ]; then gt="-gt" dest_mod="${dest_mod%%[!0-9]*}" # NB: kill trailing non-digits # for numeric comparison safety else gt=">" fi esac # Break if we've found the proper element position if [ "$curitem_haskey" -a "$dest_haskey" ]; then if [ "$dest_mod" = "$curitem_mod" ]; then [ "$dest_orig" ">" "$curitem_orig" ] && break elif [ "$dest_mod" $gt "$curitem_mod" ] \ 2> /dev/null then break fi else [ "$dest_orig" ">" "$curitem_orig" ] && break fi # Break if we've hit the end [ $d -ge $i ] && break d=$(( $d + 1 )) done # Shift remaining positions forward, making room for new item n=$i while [ $n -ge $d ]; do # Shift destination item forward one placement eval dest_$(( $n + 1 ))=\"\$dest_$n\" n=$(( $n - 1 )) done # Place the element if [ $i -eq 1 ]; then local dest_1="$curitem" else local dest_$d="$curitem" fi i=$(( $i + 1 )) done # Print sorted results d=1 while [ $d -le $nitems ]; do eval echo \"\$dest_$d\" d=$(( $d + 1 )) done } # # wait_for_pids pid [pid ...] # spins until none of the pids exist # wait_for_pids() { local _list _prefix _nlist _j _list="$@" if [ -z "$_list" ]; then return fi _prefix= while true; do _nlist=""; for _j in $_list; do if kill -0 $_j 2>/dev/null; then _nlist="${_nlist}${_nlist:+ }$_j" [ -n "$_prefix" ] && sleep 1 fi done if [ -z "$_nlist" ]; then break fi _list=$_nlist echo -n ${_prefix:-"Waiting for PIDS: "}$_list _prefix=", " pwait $_list 2>/dev/null done if [ -n "$_prefix" ]; then echo "." fi } # # get_pidfile_from_conf string file # # Takes a string to search for in the specified file. # Ignores lines with traditional comment characters. # # Example: # # if get_pidfile_from_conf string file; then # pidfile="$_pidfile_from_conf" # else # pidfile='appropriate default' # fi # get_pidfile_from_conf() { if [ -z "$1" -o -z "$2" ]; then err 3 "USAGE: get_pidfile_from_conf string file ($name)" fi local string file line string="$1" ; file="$2" if [ ! -s "$file" ]; then err 3 "get_pidfile_from_conf: $file does not exist ($name)" fi while read line; do case "$line" in *[#\;]*${string}*) continue ;; *${string}*) break ;; esac done < $file if [ -n "$line" ]; then line=${line#*/} _pidfile_from_conf="/${line%%[\"\;]*}" else return 1 fi } # # check_startmsgs # If rc_quiet is set (usually as a result of using faststart at # boot time) check if rc_startmsgs is enabled. # check_startmsgs() { if [ -n "$rc_quiet" ]; then checkyesno rc_startmsgs else return 0 fi } # # run_rc_command argument # Search for argument in the list of supported commands, which is: # "start stop restart rcvar status poll ${extra_commands}" # If there's a match, run ${argument}_cmd or the default method # (see below). # # If argument has a given prefix, then change the operation as follows: # Prefix Operation # ------ --------- # fast Skip the pid check, and set rc_fast=yes, rc_quiet=yes # force Set ${rcvar} to YES, and set rc_force=yes # one Set ${rcvar} to YES # quiet Don't output some diagnostics, and set rc_quiet=yes # # The following globals are used: # # Name Needed Purpose # ---- ------ ------- # name y Name of script. # # command n Full path to command. # Not needed if ${rc_arg}_cmd is set for # each keyword. # # command_args n Optional args/shell directives for command. # # command_interpreter n If not empty, command is interpreted, so # call check_{pidfile,process}() appropriately. # # desc n Description of script. # # extra_commands n List of extra commands supported. # # pidfile n If set, use check_pidfile $pidfile $command, # otherwise use check_process $command. # In either case, only check if $command is set. # # procname n Process name to check for instead of $command. # # rcvar n This is checked with checkyesno to determine # if the action should be run. # # ${name}_program n Full path to command. # Meant to be used in /etc/rc.conf to override # ${command}. # # ${name}_chroot n Directory to chroot to before running ${command} # Requires /usr to be mounted. # # ${name}_chdir n Directory to cd to before running ${command} # (if not using ${name}_chroot). # # ${name}_flags n Arguments to call ${command} with. # NOTE: $flags from the parent environment # can be used to override this. # # ${name}_env n Environment variables to run ${command} with. # # ${name}_env_file n File to source variables to run ${command} with. # # ${name}_fib n Routing table number to run ${command} with. # # ${name}_nice n Nice level to run ${command} at. # # ${name}_oomprotect n Don't kill ${command} when swap space is exhausted. # # ${name}_user n User to run ${command} as, using su(1) if not # using ${name}_chroot. # Requires /usr to be mounted. # # ${name}_group n Group to run chrooted ${command} as. # Requires /usr to be mounted. # # ${name}_groups n Comma separated list of supplementary groups # to run the chrooted ${command} with. # Requires /usr to be mounted. # # ${name}_prepend n Command added before ${command}. # # ${name}_login_class n Login class to use, else "daemon". # # ${name}_limits n limits(1) to apply to ${command}. # # ${rc_arg}_cmd n If set, use this as the method when invoked; # Otherwise, use default command (see below) # # ${rc_arg}_precmd n If set, run just before performing the # ${rc_arg}_cmd method in the default # operation (i.e, after checking for required # bits and process (non)existence). # If this completes with a non-zero exit code, # don't run ${rc_arg}_cmd. # # ${rc_arg}_postcmd n If set, run just after performing the # ${rc_arg}_cmd method, if that method # returned a zero exit code. # # required_dirs n If set, check for the existence of the given # directories before running a (re)start command. # # required_files n If set, check for the readability of the given # files before running a (re)start command. # # required_modules n If set, ensure the given kernel modules are # loaded before running a (re)start command. # The check and possible loads are actually # done after start_precmd so that the modules # aren't loaded in vain, should the precmd # return a non-zero status to indicate a error. # If a word in the list looks like "foo:bar", # "foo" is the KLD file name and "bar" is the # module name. If a word looks like "foo~bar", # "foo" is the KLD file name and "bar" is a # egrep(1) pattern matching the module name. # Otherwise the module name is assumed to be # the same as the KLD file name, which is most # common. See load_kld(). # # required_vars n If set, perform checkyesno on each of the # listed variables before running the default # (re)start command. # # Default behaviour for a given argument, if no override method is # provided: # # Argument Default behaviour # -------- ----------------- # start if !running && checkyesno ${rcvar} # ${command} # # stop if ${pidfile} # rc_pid=$(check_pidfile $pidfile $command) # else # rc_pid=$(check_process $command) # kill $sig_stop $rc_pid # wait_for_pids $rc_pid # ($sig_stop defaults to TERM.) # # reload Similar to stop, except use $sig_reload instead, # and doesn't wait_for_pids. # $sig_reload defaults to HUP. # Note that `reload' isn't provided by default, # it should be enabled via $extra_commands. # # restart Run `stop' then `start'. # # status Show if ${command} is running, etc. # # poll Wait for ${command} to exit. # # rcvar Display what rc.conf variable is used (if any). # # enabled Return true if the service is enabled. # # describe Show the service's description # # extracommands Show the service's extra commands # # Variables available to methods, and after run_rc_command() has # completed: # # Variable Purpose # -------- ------- # rc_arg Argument to command, after fast/force/one processing # performed # # rc_flags Flags to start the default command with. # Defaults to ${name}_flags, unless overridden # by $flags from the environment. # This variable may be changed by the precmd method. # # rc_pid PID of command (if appropriate) # # rc_fast Not empty if "fast" was provided (q.v.) # # rc_force Not empty if "force" was provided (q.v.) # # rc_quiet Not empty if "quiet" was provided # # run_rc_command() { _return=0 rc_arg=$1 if [ -z "$name" ]; then err 3 'run_rc_command: $name is not set.' fi # Don't repeat the first argument when passing additional command- # line arguments to the command subroutines. # shift 1 rc_extra_args="$*" _rc_prefix= case "$rc_arg" in fast*) # "fast" prefix; don't check pid rc_arg=${rc_arg#fast} rc_fast=yes rc_quiet=yes ;; force*) # "force" prefix; always run rc_force=yes _rc_prefix=force rc_arg=${rc_arg#${_rc_prefix}} if [ -n "${rcvar}" ]; then eval ${rcvar}=YES fi ;; one*) # "one" prefix; set ${rcvar}=yes _rc_prefix=one rc_arg=${rc_arg#${_rc_prefix}} if [ -n "${rcvar}" ]; then eval ${rcvar}=YES fi ;; quiet*) # "quiet" prefix; omit some messages _rc_prefix=quiet rc_arg=${rc_arg#${_rc_prefix}} rc_quiet=yes ;; esac eval _override_command=\$${name}_program command=${_override_command:-$command} _keywords="start stop restart rcvar enable disable delete enabled describe extracommands $extra_commands" rc_pid= _pidcmd= _procname=${procname:-${command}} # setup pid check command if [ -n "$_procname" ]; then if [ -n "$pidfile" ]; then _pidcmd='rc_pid=$(check_pidfile '"$pidfile $_procname $command_interpreter"')' else _pidcmd='rc_pid=$(check_process '"$_procname $command_interpreter"')' fi _keywords="${_keywords} status poll" fi if [ -z "$rc_arg" ]; then rc_usage $_keywords fi if [ "$rc_arg" = "enabled" ] ; then checkyesno ${rcvar} return $? fi if [ -n "$flags" ]; then # allow override from environment rc_flags=$flags else eval rc_flags=\$${name}_flags fi eval _chdir=\$${name}_chdir _chroot=\$${name}_chroot \ _nice=\$${name}_nice _user=\$${name}_user \ _group=\$${name}_group _groups=\$${name}_groups \ _fib=\$${name}_fib _env=\$${name}_env \ _prepend=\$${name}_prepend _login_class=\${${name}_login_class:-daemon} \ _limits=\$${name}_limits _oomprotect=\$${name}_oomprotect \ _env_file=\$${name}_env_file if [ -n "$_env_file" ] && [ -r "${_env_file}" ]; then # load env from file set -a . $_env_file set +a fi if [ -n "$_user" ]; then # unset $_user if running as that user if [ "$_user" = "$(eval $IDCMD)" ]; then unset _user fi fi [ -z "$autoboot" ] && eval $_pidcmd # determine the pid if necessary for _elem in $_keywords; do if [ "$_elem" != "$rc_arg" ]; then continue fi # if ${rcvar} is set, $1 is not "rcvar", "describe", # "enable" or "delete", and ${rc_pid} is not set, run: # checkyesno ${rcvar} # and return if that failed # if [ -n "${rcvar}" -a "$rc_arg" != "rcvar" -a "$rc_arg" != "stop" \ -a "$rc_arg" != "delete" -a "$rc_arg" != "enable" \ -a "$rc_arg" != "describe" ] || [ -n "${rcvar}" -a "$rc_arg" = "stop" -a -z "${rc_pid}" ]; then if ! checkyesno ${rcvar}; then if [ -n "${rc_quiet}" ]; then return 0 fi echo -n "Cannot '${rc_arg}' $name. Set ${rcvar} to " echo -n "YES in /etc/rc.conf or use 'one${rc_arg}' " echo "instead of '${rc_arg}'." return 0 fi fi if [ $rc_arg = "start" -a -z "$rc_fast" -a -n "$rc_pid" ]; then if [ -z "$rc_quiet" ]; then echo 1>&2 "${name} already running? " \ "(pid=$rc_pid)." fi return 1 fi # if there's a custom ${XXX_cmd}, # run that instead of the default # eval _cmd=\$${rc_arg}_cmd \ _precmd=\$${rc_arg}_precmd \ _postcmd=\$${rc_arg}_postcmd if [ -n "$_cmd" ]; then _run_rc_precmd || return 1 _run_rc_doit "$_cmd $rc_extra_args" || return 1 _run_rc_postcmd return $_return fi case "$rc_arg" in # default operations... describe) if [ -n "$desc" ]; then echo "$desc" fi ;; extracommands) echo "$extra_commands" ;; enable) _out=$(/usr/sbin/sysrc -vs "$name" "$rcvar=YES") && echo "$name enabled in ${_out%%:*}" ;; disable) _out=$(/usr/sbin/sysrc -vs "$name" "$rcvar=NO") && echo "$name disabled in ${_out%%:*}" ;; delete) _files= for _file in $(sysrc -lEs "$name"); do _out=$(sysrc -Fif $_file "$rcvar") && _files="$_files $_file" done /usr/sbin/sysrc -x "$rcvar" && echo "$rcvar deleted in ${_files# }" # delete file in rc.conf.d if desired and empty. checkyesno service_delete_empty || _files= for _file in $_files; do [ "$_file" = "${_file#*/rc.conf.d/}" ] && continue [ $(/usr/bin/stat -f%z $_file) -gt 0 ] && continue /bin/rm "$_file" && echo "Empty file $_file removed" done ;; status) _run_rc_precmd || return 1 if [ -n "$rc_pid" ]; then echo "${name} is running as pid $rc_pid." else echo "${name} is not running." return 1 fi _run_rc_postcmd ;; start) if [ ! -x "${_chroot}${_chroot:+/}${command}" ]; then warn "run_rc_command: cannot run $command" return 1 fi if ! _run_rc_precmd; then warn "failed precmd routine for ${name}" return 1 fi # setup the full command to run # check_startmsgs && echo "Starting ${name}." if [ -n "$_chroot" ]; then _cd= _doit="\ ${_nice:+nice -n $_nice }\ ${_fib:+setfib -F $_fib }\ ${_env:+env $_env }\ chroot ${_user:+-u $_user }${_group:+-g $_group }${_groups:+-G $_groups }\ $_chroot $command $rc_flags $command_args" else _cd="${_chdir:+cd $_chdir && }" _doit="\ ${_fib:+setfib -F $_fib }\ ${_env:+env $_env }\ $command $rc_flags $command_args" if [ -n "$_user" ]; then _doit="su -m $_user -c 'sh -c \"$_doit\"'" fi if [ -n "$_nice" ]; then if [ -z "$_user" ]; then _doit="sh -c \"$_doit\"" fi _doit="nice -n $_nice $_doit" fi if [ -n "$_prepend" ]; then _doit="$_prepend $_doit" fi fi # Prepend default limits _doit="$_cd limits -C $_login_class $_limits $_doit" # run the full command # if ! _run_rc_doit "$_doit"; then warn "failed to start ${name}" return 1 fi # finally, run postcmd # _run_rc_postcmd ;; stop) if [ -z "$rc_pid" ]; then [ -n "$rc_fast" ] && return 0 _run_rc_notrunning return 1 fi _run_rc_precmd || return 1 # send the signal to stop # echo "Stopping ${name}." _doit=$(_run_rc_killcmd "${sig_stop:-TERM}") _run_rc_doit "$_doit" || return 1 # wait for the command to exit, # and run postcmd. wait_for_pids $rc_pid _run_rc_postcmd ;; reload) if [ -z "$rc_pid" ]; then _run_rc_notrunning return 1 fi _run_rc_precmd || return 1 _doit=$(_run_rc_killcmd "${sig_reload:-HUP}") _run_rc_doit "$_doit" || return 1 _run_rc_postcmd ;; restart) # prevent restart being called more # than once by any given script # if ${_rc_restart_done:-false}; then return 0 fi _rc_restart_done=true _run_rc_precmd || return 1 # run those in a subshell to keep global variables ( run_rc_command ${_rc_prefix}stop $rc_extra_args ) ( run_rc_command ${_rc_prefix}start $rc_extra_args ) _return=$? [ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1 _run_rc_postcmd ;; poll) _run_rc_precmd || return 1 if [ -n "$rc_pid" ]; then wait_for_pids $rc_pid fi _run_rc_postcmd ;; rcvar) echo -n "# $name" if [ -n "$desc" ]; then echo " : $desc" else echo "" fi echo "#" # Get unique vars in $rcvar $rcvars for _v in $rcvar $rcvars; do case $v in $_v\ *|\ *$_v|*\ $_v\ *) ;; *) v="${v# } $_v" ;; esac done # Display variables. for _v in $v; do if [ -z "$_v" ]; then continue fi eval _desc=\$${_v}_desc eval _defval=\$${_v}_defval _h="-" eval echo \"$_v=\\\"\$$_v\\\"\" # decode multiple lines of _desc while [ -n "$_desc" ]; do case $_desc in *^^*) echo "# $_h ${_desc%%^^*}" _desc=${_desc#*^^} _h=" " ;; *) echo "# $_h ${_desc}" break ;; esac done echo "# (default: \"$_defval\")" done echo "" ;; *) rc_usage $_keywords ;; esac # Apply protect(1) to the PID if ${name}_oomprotect is set. case "$rc_arg" in start) # We cannot use protect(1) inside jails. if [ -n "$_oomprotect" ] && [ -f "${PROTECT}" ] && [ "$(sysctl -n security.jail.jailed)" -eq 0 ]; then pid=$(check_process $command) case $_oomprotect in [Aa][Ll][Ll]) ${PROTECT} -i -p ${pid} ;; [Yy][Ee][Ss]) ${PROTECT} -p ${pid} ;; esac fi ;; esac return $_return done echo 1>&2 "$0: unknown directive '$rc_arg'." rc_usage $_keywords # not reached } # # Helper functions for run_rc_command: common code. # They use such global variables besides the exported rc_* ones: # # name R/W # ------------------ # _precmd R # _postcmd R # _return W # _run_rc_precmd() { check_required_before "$rc_arg" || return 1 if [ -n "$_precmd" ]; then debug "run_rc_command: ${rc_arg}_precmd: $_precmd $rc_extra_args" eval "$_precmd $rc_extra_args" _return=$? # If precmd failed and force isn't set, request exit. if [ $_return -ne 0 ] && [ -z "$rc_force" ]; then return 1 fi fi check_required_after "$rc_arg" || return 1 return 0 } _run_rc_postcmd() { if [ -n "$_postcmd" ]; then debug "run_rc_command: ${rc_arg}_postcmd: $_postcmd $rc_extra_args" eval "$_postcmd $rc_extra_args" _return=$? fi return 0 } _run_rc_doit() { debug "run_rc_command: doit: $*" eval "$@" _return=$? # If command failed and force isn't set, request exit. if [ $_return -ne 0 ] && [ -z "$rc_force" ]; then return 1 fi return 0 } _run_rc_notrunning() { local _pidmsg if [ -n "$pidfile" ]; then _pidmsg=" (check $pidfile)." else _pidmsg= fi echo 1>&2 "${name} not running?${_pidmsg}" } _run_rc_killcmd() { local _cmd _cmd="kill -$1 $rc_pid" if [ -n "$_user" ]; then _cmd="su -m ${_user} -c 'sh -c \"${_cmd}\"'" fi echo "$_cmd" } # # run_rc_script file arg # Start the script `file' with `arg', and correctly handle the # return value from the script. # If `file' ends with `.sh' and lives in /etc/rc.d, ignore it as it's # an old-style startup file. # If `file' ends with `.sh' and does not live in /etc/rc.d, it's sourced # into the current environment if $rc_fast_and_loose is set; otherwise # it is run as a child process. # If `file' appears to be a backup or scratch file, ignore it. # Otherwise if it is executable run as a child process. # run_rc_script() { _file=$1 _arg=$2 if [ -z "$_file" -o -z "$_arg" ]; then err 3 'USAGE: run_rc_script file arg' fi unset name command command_args command_interpreter \ extra_commands pidfile procname \ rcvar rcvars rcvars_obsolete required_dirs required_files \ required_vars eval unset ${_arg}_cmd ${_arg}_precmd ${_arg}_postcmd case "$_file" in /etc/rc.d/*.sh) # no longer allowed in the base warn "Ignoring old-style startup script $_file" ;; *[~#]|*.OLD|*.bak|*.orig|*,v) # scratch file; skip warn "Ignoring scratch file $_file" ;; *) # run in subshell if [ -x $_file ]; then if [ -n "$rc_fast_and_loose" ]; then set $_arg; . $_file else ( trap "echo Script $_file interrupted >&2 ; kill -QUIT $$" 3 trap "echo Script $_file interrupted >&2 ; exit 1" 2 trap "echo Script $_file running >&2" 29 set $_arg; . $_file ) fi fi ;; esac } # # load_rc_config [service] # Source in the configuration file(s) for a given service. # If no service is specified, only the global configuration # file(s) will be loaded. # load_rc_config() { local _name _rcvar_val _var _defval _v _msg _new _d _name=$1 if ${_rc_conf_loaded:-false}; then : else if [ -r /etc/defaults/rc.conf ]; then debug "Sourcing /etc/defaults/rc.conf" . /etc/defaults/rc.conf source_rc_confs elif [ -r /etc/rc.conf ]; then debug "Sourcing /etc/rc.conf (/etc/defaults/rc.conf doesn't exist)." . /etc/rc.conf fi _rc_conf_loaded=true fi # If a service name was specified, attempt to load # service-specific configuration if [ -n "$_name" ] ; then for _d in /etc ${local_startup}; do _d=${_d%/rc.d} if [ -f ${_d}/rc.conf.d/"$_name" ]; then debug "Sourcing ${_d}/rc.conf.d/$_name" . ${_d}/rc.conf.d/"$_name" elif [ -d ${_d}/rc.conf.d/"$_name" ] ; then local _rc for _rc in ${_d}/rc.conf.d/"$_name"/* ; do if [ -f "$_rc" ] ; then debug "Sourcing $_rc" . "$_rc" fi done fi done fi # Set defaults if defined. for _var in $rcvar $rcvars; do eval _defval=\$${_var}_defval if [ -n "$_defval" ]; then eval : \${$_var:=\$${_var}_defval} fi done # check obsolete rc.conf variables for _var in $rcvars_obsolete; do eval _v=\$$_var eval _msg=\$${_var}_obsolete_msg eval _new=\$${_var}_newvar case $_v in "") ;; *) if [ -z "$_new" ]; then _msg="Ignored." else eval $_new=\"\$$_var\" if [ -z "$_msg" ]; then _msg="Use \$$_new instead." fi fi warn "\$$_var is obsolete. $_msg" ;; esac done } # # load_rc_config_var name var # Read the rc.conf(5) var for name and set in the # current shell, using load_rc_config in a subshell to prevent # unwanted side effects from other variable assignments. # load_rc_config_var() { if [ $# -ne 2 ]; then err 3 'USAGE: load_rc_config_var name var' fi eval $(eval '( load_rc_config '$1' >/dev/null; if [ -n "${'$2'}" -o "${'$2'-UNSET}" != "UNSET" ]; then echo '$2'=\'\''${'$2'}\'\''; fi )' ) } # # rc_usage commands # Print a usage string for $0, with `commands' being a list of # valid commands. # rc_usage() { echo -n 1>&2 "Usage: $0 [fast|force|one|quiet](" _sep= for _elem; do echo -n 1>&2 "$_sep$_elem" _sep="|" done echo 1>&2 ")" exit 1 } # # err exitval message # Display message to stderr and log to the syslog, and exit with exitval. # err() { exitval=$1 shift if [ -x /usr/bin/logger ]; then logger "$0: ERROR: $*" fi echo 1>&2 "$0: ERROR: $*" exit $exitval } # # warn message # Display message to stderr and log to the syslog. # warn() { if [ -x /usr/bin/logger ]; then logger "$0: WARNING: $*" fi echo 1>&2 "$0: WARNING: $*" } # # info message # Display informational message to stdout and log to syslog. # info() { case ${rc_info} in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) if [ -x /usr/bin/logger ]; then logger "$0: INFO: $*" fi echo "$0: INFO: $*" ;; esac } # # debug message # If debugging is enabled in rc.conf output message to stderr. # BEWARE that you don't call any subroutine that itself calls this # function. # debug() { case ${rc_debug} in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) if [ -x /usr/bin/logger ]; then logger "$0: DEBUG: $*" fi echo 1>&2 "$0: DEBUG: $*" ;; esac } # # backup_file action file cur backup # Make a backup copy of `file' into `cur', and save the previous # version of `cur' as `backup'. # # The `action' keyword can be one of the following: # # add `file' is now being backed up (and is possibly # being reentered into the backups system). `cur' # is created. # # update `file' has changed and needs to be backed up. # If `cur' exists, it is copied to `back' # and then `file' is copied to `cur'. # # remove `file' is no longer being tracked by the backups # system. `cur' is moved `back'. # # backup_file() { _action=$1 _file=$2 _cur=$3 _back=$4 case $_action in add|update) if [ -f $_cur ]; then cp -p $_cur $_back fi cp -p $_file $_cur chown root:wheel $_cur ;; remove) mv -f $_cur $_back ;; esac } # make_symlink src link # Make a symbolic link 'link' to src from basedir. If the # directory in which link is to be created does not exist # a warning will be displayed and an error will be returned. # Returns 0 on success, 1 otherwise. # make_symlink() { local src link linkdir _me src="$1" link="$2" linkdir="`dirname $link`" _me="make_symlink()" if [ -z "$src" -o -z "$link" ]; then warn "$_me: requires two arguments." return 1 fi if [ ! -d "$linkdir" ]; then warn "$_me: the directory $linkdir does not exist." return 1 fi if ! ln -sf $src $link; then warn "$_me: unable to make a symbolic link from $link to $src" return 1 fi return 0 } # devfs_rulesets_from_file file # Reads a set of devfs commands from file, and creates # the specified rulesets with their rules. Returns non-zero # if there was an error. # devfs_rulesets_from_file() { local file _err _me _opts file="$1" _me="devfs_rulesets_from_file" _err=0 if [ -z "$file" ]; then warn "$_me: you must specify a file" return 1 fi if [ ! -e "$file" ]; then debug "$_me: no such file ($file)" return 0 fi # Disable globbing so that the rule patterns are not expanded # by accident with matching filesystem entries. _opts=$-; set -f debug "reading rulesets from file ($file)" { while read line do case $line in \#*) continue ;; \[*\]*) rulenum=`expr "$line" : "\[.*=\([0-9]*\)\]"` if [ -z "$rulenum" ]; then warn "$_me: cannot extract rule number ($line)" _err=1 break fi rulename=`expr "$line" : "\[\(.*\)=[0-9]*\]"` if [ -z "$rulename" ]; then warn "$_me: cannot extract rule name ($line)" _err=1 break; fi eval $rulename=\$rulenum debug "found ruleset: $rulename=$rulenum" if ! /sbin/devfs rule -s $rulenum delset; then _err=1 break fi ;; *) rulecmd="${line%%"\#*"}" # evaluate the command incase it includes # other rules if [ -n "$rulecmd" ]; then debug "adding rule ($rulecmd)" if ! eval /sbin/devfs rule -s $rulenum $rulecmd then _err=1 break fi fi ;; esac if [ $_err -ne 0 ]; then debug "error in $_me" break fi done } < $file case $_opts in *f*) ;; *) set +f ;; esac return $_err } # devfs_init_rulesets # Initializes rulesets from configuration files. Returns # non-zero if there was an error. # devfs_init_rulesets() { local file _me _me="devfs_init_rulesets" # Go through this only once if [ -n "$devfs_rulesets_init" ]; then debug "$_me: devfs rulesets already initialized" return fi for file in $devfs_rulesets; do if ! devfs_rulesets_from_file $file; then warn "$_me: could not read rules from $file" return 1 fi done devfs_rulesets_init=1 debug "$_me: devfs rulesets initialized" return 0 } # devfs_set_ruleset ruleset [dir] # Sets the default ruleset of dir to ruleset. The ruleset argument # must be a ruleset name as specified in devfs.rules(5) file. # Returns non-zero if it could not set it successfully. # devfs_set_ruleset() { local devdir rs _me [ -n "$1" ] && eval rs=\$$1 || rs= [ -n "$2" ] && devdir="-m "$2"" || devdir= _me="devfs_set_ruleset" if [ -z "$rs" ]; then warn "$_me: you must specify a ruleset number" return 1 fi debug "$_me: setting ruleset ($rs) on mount-point (${devdir#-m })" if ! /sbin/devfs $devdir ruleset $rs; then warn "$_me: unable to set ruleset $rs to ${devdir#-m }" return 1 fi return 0 } # devfs_apply_ruleset ruleset [dir] # Apply ruleset number $ruleset to the devfs mountpoint $dir. # The ruleset argument must be a ruleset name as specified # in a devfs.rules(5) file. Returns 0 on success or non-zero # if it could not apply the ruleset. # devfs_apply_ruleset() { local devdir rs _me [ -n "$1" ] && eval rs=\$$1 || rs= [ -n "$2" ] && devdir="-m "$2"" || devdir= _me="devfs_apply_ruleset" if [ -z "$rs" ]; then warn "$_me: you must specify a ruleset" return 1 fi debug "$_me: applying ruleset ($rs) to mount-point (${devdir#-m })" if ! /sbin/devfs $devdir rule -s $rs applyset; then warn "$_me: unable to apply ruleset $rs to ${devdir#-m }" return 1 fi return 0 } # devfs_domount dir [ruleset] # Mount devfs on dir. If ruleset is specified it is set # on the mount-point. It must also be a ruleset name as specified # in a devfs.rules(5) file. Returns 0 on success. # devfs_domount() { local devdir rs _me devdir="$1" [ -n "$2" ] && rs=$2 || rs= _me="devfs_domount()" if [ -z "$devdir" ]; then warn "$_me: you must specify a mount-point" return 1 fi debug "$_me: mount-point is ($devdir), ruleset is ($rs)" if ! mount -t devfs dev "$devdir"; then warn "$_me: Unable to mount devfs on $devdir" return 1 fi if [ -n "$rs" ]; then devfs_init_rulesets devfs_set_ruleset $rs $devdir devfs -m $devdir rule applyset fi return 0 } # Provide a function for normalizing the mounting of memory # filesystems. This should allow the rest of the code here to remain # as close as possible between 5-current and 4-stable. # $1 = size # $2 = mount point # $3 = (optional) extra mdmfs flags mount_md() { if [ -n "$3" ]; then flags="$3" fi /sbin/mdmfs $flags -s $1 ${mfs_type} $2 } # Code common to scripts that need to load a kernel module # if it isn't in the kernel yet. Syntax: # load_kld [-e regex] [-m module] file # where -e or -m chooses the way to check if the module # is already loaded: # regex is egrep'd in the output from `kldstat -v', # module is passed to `kldstat -m'. # The default way is as though `-m file' were specified. load_kld() { local _loaded _mod _opt _re while getopts "e:m:" _opt; do case "$_opt" in e) _re="$OPTARG" ;; m) _mod="$OPTARG" ;; *) err 3 'USAGE: load_kld [-e regex] [-m module] file' ;; esac done shift $(($OPTIND - 1)) if [ $# -ne 1 ]; then err 3 'USAGE: load_kld [-e regex] [-m module] file' fi _mod=${_mod:-$1} _loaded=false if [ -n "$_re" ]; then if kldstat -v | egrep -q -e "$_re"; then _loaded=true fi else if kldstat -q -m "$_mod"; then _loaded=true fi fi if ! $_loaded; then if ! kldload "$1"; then warn "Unable to load kernel module $1" return 1 else info "$1 kernel module loaded." fi else debug "load_kld: $1 kernel module already loaded." fi return 0 } # ltr str src dst [var] # Change every $src in $str to $dst. # Useful when /usr is not yet mounted and we cannot use tr(1), sed(1) nor # awk(1). If var is non-NULL, set it to the result. ltr() { local _str _src _dst _out _com _var _str="$1" _src="$2" _dst="$3" _var="$4" _out="" local IFS="${_src}" for _com in ${_str}; do if [ -z "${_out}" ]; then _out="${_com}" else _out="${_out}${_dst}${_com}" fi done if [ -n "${_var}" ]; then setvar "${_var}" "${_out}" else echo "${_out}" fi } # Creates a list of providers for GELI encryption. geli_make_list() { local devices devices2 local provider mountpoint type options rest # Create list of GELI providers from fstab. while read provider mountpoint type options rest ; do case ":${options}" in :*noauto*) noauto=yes ;; *) noauto=no ;; esac case ":${provider}" in :#*) continue ;; *.eli) # Skip swap devices. if [ "${type}" = "swap" -o "${options}" = "sw" -o "${noauto}" = "yes" ]; then continue fi devices="${devices} ${provider}" ;; esac done < /etc/fstab # Append providers from geli_devices. devices="${devices} ${geli_devices}" for provider in ${devices}; do provider=${provider%.eli} provider=${provider#/dev/} devices2="${devices2} ${provider}" done echo ${devices2} } # Originally, root mount hold had to be released before mounting # the root filesystem. This delayed the boot, so it was changed # to only wait if the root device isn't readily available. This # can result in rc scripts executing before all the devices - such # as graid(8), or USB disks - can be accessed. This function can # be used to explicitly wait for root mount holds to be released. root_hold_wait() { local wait waited holders waited=0 while true; do holders="$(sysctl -n vfs.root_mount_hold)" if [ -z "${holders}" ]; then break; fi if [ ${waited} -eq 0 ]; then echo -n "Waiting ${root_hold_delay}s" \ "for the root mount holders: ${holders}" else echo -n . fi if [ ${waited} -ge ${root_hold_delay} ]; then echo break fi sleep 1 waited=$(($waited + 1)) done } # Find scripts in local_startup directories that use the old syntax # find_local_scripts_old() { zlist='' slist='' for dir in ${local_startup}; do if [ -d "${dir}" ]; then for file in ${dir}/[0-9]*.sh; do grep '^# PROVIDE:' $file >/dev/null 2>&1 && continue zlist="$zlist $file" done for file in ${dir}/[!0-9]*.sh; do grep '^# PROVIDE:' $file >/dev/null 2>&1 && continue slist="$slist $file" done fi done } find_local_scripts_new() { local_rc='' for dir in ${local_startup}; do if [ -d "${dir}" ]; then for file in `grep -l '^# PROVIDE:' ${dir}/* 2>/dev/null`; do case "$file" in *.sample) ;; *) if [ -x "$file" ]; then local_rc="${local_rc} ${file}" fi ;; esac done fi done } # check_required_{before|after} command # Check for things required by the command before and after its precmd, # respectively. The two separate functions are needed because some # conditions should prevent precmd from being run while other things # depend on precmd having already been run. # check_required_before() { local _f case "$1" in start) for _f in $required_vars; do if ! checkyesno $_f; then warn "\$${_f} is not enabled." if [ -z "$rc_force" ]; then return 1 fi fi done for _f in $required_dirs; do if [ ! -d "${_f}/." ]; then warn "${_f} is not a directory." if [ -z "$rc_force" ]; then return 1 fi fi done for _f in $required_files; do if [ ! -r "${_f}" ]; then warn "${_f} is not readable." if [ -z "$rc_force" ]; then return 1 fi fi done ;; esac return 0 } check_required_after() { local _f _args case "$1" in start) for _f in $required_modules; do case "${_f}" in *~*) _args="-e ${_f#*~} ${_f%%~*}" ;; *:*) _args="-m ${_f#*:} ${_f%%:*}" ;; *) _args="${_f}" ;; esac if ! load_kld ${_args}; then if [ -z "$rc_force" ]; then return 1 fi fi done ;; esac return 0 } # check_jail mib # Return true if security.jail.$mib exists and set to 1. check_jail() { local _mib _v _mib=$1 if _v=$(${SYSCTL_N} "security.jail.$_mib" 2> /dev/null); then case $_v in 1) return 0;; esac fi return 1 } # check_kern_features mib # Return existence of kern.features.* sysctl MIB as true or # false. The result will be cached in $_rc_cache_kern_features_ # namespace. "0" means the kern.features.X exists. check_kern_features() { local _v [ -n "$1" ] || return 1; eval _v=\$_rc_cache_kern_features_$1 [ -n "$_v" ] && return "$_v"; if ${SYSCTL_N} kern.features.$1 > /dev/null 2>&1; then eval _rc_cache_kern_features_$1=0 return 0 else eval _rc_cache_kern_features_$1=1 return 1 fi } # check_namevarlist var # Return "0" if ${name}_var is reserved in rc.subr. _rc_namevarlist="program chroot chdir env flags fib nice user group groups prepend" check_namevarlist() { local _v for _v in $_rc_namevarlist; do case $1 in $_v) return 0 ;; esac done return 1 } # _echoonce var msg mode # mode=0: Echo $msg if ${$var} is empty. # After doing echo, a string is set to ${$var}. # # mode=1: Echo $msg if ${$var} is a string with non-zero length. # _echoonce() { local _var _msg _mode eval _var=\$$1 _msg=$2 _mode=$3 case $_mode in 1) [ -n "$_var" ] && echo "$_msg" ;; *) [ -z "$_var" ] && echo -n "$_msg" && eval "$1=finished" ;; esac } # If the loader env variable rc.debug is set, turn on debugging. rc.conf will # still override this, but /etc/defaults/rc.conf can't unconditionally set this # since it would undo what we've done here. if kenv -q rc.debug > /dev/null ; then rc_debug=YES fi Index: projects/clang700-import/sbin/ipfw/ipfw2.c =================================================================== --- projects/clang700-import/sbin/ipfw/ipfw2.c (revision 341812) +++ projects/clang700-import/sbin/ipfw/ipfw2.c (revision 341813) @@ -1,5584 +1,5581 @@ /*- * Copyright (c) 2002-2003 Luigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp * Copyright (c) 1994 Ugen J.S.Antsilevich * * Idea and grammar partially left from: * Copyright (c) 1993 Daniel Boulet * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * NEW command line interface for IP firewall facility * * $FreeBSD$ */ #include #include #include #include #include #include "ipfw2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ctime */ #include /* _long_to_time */ #include #include #include /* offsetof */ #include #include /* only IFNAMSIZ */ #include #include /* only n_short, n_long */ #include #include #include #include #include struct cmdline_opts co; /* global options */ struct format_opts { int bcwidth; int pcwidth; int show_counters; int show_time; /* show timestamp */ uint32_t set_mask; /* enabled sets mask */ uint32_t flags; /* request flags */ uint32_t first; /* first rule to request */ uint32_t last; /* last rule to request */ uint32_t dcnt; /* number of dynamic states */ ipfw_obj_ctlv *tstate; /* table state data */ }; int resvd_set_number = RESVD_SET; int ipfw_socket = -1; #define CHECK_LENGTH(v, len) do { \ if ((v) < (len)) \ errx(EX_DATAERR, "Rule too long"); \ } while (0) /* * Check if we have enough space in cmd buffer. Note that since * first 8? u32 words are reserved by reserved header, full cmd * buffer can't be used, so we need to protect from buffer overrun * only. At the beginning, cblen is less than actual buffer size by * size of ipfw_insn_u32 instruction + 1 u32 work. This eliminates need * for checking small instructions fitting in given range. * We also (ab)use the fact that ipfw_insn is always the first field * for any custom instruction. */ #define CHECK_CMDLEN CHECK_LENGTH(cblen, F_LEN((ipfw_insn *)cmd)) #define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ if (!av[0]) \ errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ if (_substrcmp(*av, "tablearg") == 0) { \ arg = IP_FW_TARG; \ break; \ } \ \ { \ long _xval; \ char *end; \ \ _xval = strtol(*av, &end, 10); \ \ if (!isdigit(**av) || *end != '\0' || (_xval == 0 && errno == EINVAL)) \ errx(EX_DATAERR, "%s: invalid argument: %s", \ match_value(s_x, tok), *av); \ \ if (errno == ERANGE || _xval < min || _xval > max) \ errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \ match_value(s_x, tok), min, max, *av); \ \ if (_xval == IP_FW_TARG) \ errx(EX_DATAERR, "%s: illegal argument value: %s", \ match_value(s_x, tok), *av); \ arg = _xval; \ } \ } while (0) static struct _s_x f_tcpflags[] = { { "syn", TH_SYN }, { "fin", TH_FIN }, { "ack", TH_ACK }, { "psh", TH_PUSH }, { "rst", TH_RST }, { "urg", TH_URG }, { "tcp flag", 0 }, { NULL, 0 } }; static struct _s_x f_tcpopts[] = { { "mss", IP_FW_TCPOPT_MSS }, { "maxseg", IP_FW_TCPOPT_MSS }, { "window", IP_FW_TCPOPT_WINDOW }, { "sack", IP_FW_TCPOPT_SACK }, { "ts", IP_FW_TCPOPT_TS }, { "timestamp", IP_FW_TCPOPT_TS }, { "cc", IP_FW_TCPOPT_CC }, { "tcp option", 0 }, { NULL, 0 } }; /* * IP options span the range 0 to 255 so we need to remap them * (though in fact only the low 5 bits are significant). */ static struct _s_x f_ipopts[] = { { "ssrr", IP_FW_IPOPT_SSRR}, { "lsrr", IP_FW_IPOPT_LSRR}, { "rr", IP_FW_IPOPT_RR}, { "ts", IP_FW_IPOPT_TS}, { "ip option", 0 }, { NULL, 0 } }; static struct _s_x f_iptos[] = { { "lowdelay", IPTOS_LOWDELAY}, { "throughput", IPTOS_THROUGHPUT}, { "reliability", IPTOS_RELIABILITY}, { "mincost", IPTOS_MINCOST}, { "congestion", IPTOS_ECN_CE}, { "ecntransport", IPTOS_ECN_ECT0}, { "ip tos option", 0}, { NULL, 0 } }; struct _s_x f_ipdscp[] = { { "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */ { "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */ { "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */ { "af21", IPTOS_DSCP_AF21 >> 2 }, /* 010010 */ { "af22", IPTOS_DSCP_AF22 >> 2 }, /* 010100 */ { "af23", IPTOS_DSCP_AF23 >> 2 }, /* 010110 */ { "af31", IPTOS_DSCP_AF31 >> 2 }, /* 011010 */ { "af32", IPTOS_DSCP_AF32 >> 2 }, /* 011100 */ { "af33", IPTOS_DSCP_AF33 >> 2 }, /* 011110 */ { "af41", IPTOS_DSCP_AF41 >> 2 }, /* 100010 */ { "af42", IPTOS_DSCP_AF42 >> 2 }, /* 100100 */ { "af43", IPTOS_DSCP_AF43 >> 2 }, /* 100110 */ { "be", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */ { "ef", IPTOS_DSCP_EF >> 2 }, /* 101110 */ { "cs0", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */ { "cs1", IPTOS_DSCP_CS1 >> 2 }, /* 001000 */ { "cs2", IPTOS_DSCP_CS2 >> 2 }, /* 010000 */ { "cs3", IPTOS_DSCP_CS3 >> 2 }, /* 011000 */ { "cs4", IPTOS_DSCP_CS4 >> 2 }, /* 100000 */ { "cs5", IPTOS_DSCP_CS5 >> 2 }, /* 101000 */ { "cs6", IPTOS_DSCP_CS6 >> 2 }, /* 110000 */ { "cs7", IPTOS_DSCP_CS7 >> 2 }, /* 100000 */ { NULL, 0 } }; static struct _s_x limit_masks[] = { {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT}, {"src-addr", DYN_SRC_ADDR}, {"src-port", DYN_SRC_PORT}, {"dst-addr", DYN_DST_ADDR}, {"dst-port", DYN_DST_PORT}, {NULL, 0} }; /* * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines * This is only used in this code. */ #define IPPROTO_ETHERTYPE 0x1000 static struct _s_x ether_types[] = { /* * Note, we cannot use "-:&/" in the names because they are field * separators in the type specifications. Also, we use s = NULL as * end-delimiter, because a type of 0 can be legal. */ { "ip", 0x0800 }, { "ipv4", 0x0800 }, { "ipv6", 0x86dd }, { "arp", 0x0806 }, { "rarp", 0x8035 }, { "vlan", 0x8100 }, { "loop", 0x9000 }, { "trail", 0x1000 }, { "at", 0x809b }, { "atalk", 0x809b }, { "aarp", 0x80f3 }, { "pppoe_disc", 0x8863 }, { "pppoe_sess", 0x8864 }, { "ipx_8022", 0x00E0 }, { "ipx_8023", 0x0000 }, { "ipx_ii", 0x8137 }, { "ipx_snap", 0x8137 }, { "ipx", 0x8137 }, { "ns", 0x0600 }, { NULL, 0 } }; static struct _s_x rule_eactions[] = { { "nat64lsn", TOK_NAT64LSN }, { "nat64stl", TOK_NAT64STL }, { "nptv6", TOK_NPTV6 }, { "tcp-setmss", TOK_TCPSETMSS }, { NULL, 0 } /* terminator */ }; static struct _s_x rule_actions[] = { { "abort6", TOK_ABORT6 }, { "abort", TOK_ABORT }, { "accept", TOK_ACCEPT }, { "pass", TOK_ACCEPT }, { "allow", TOK_ACCEPT }, { "permit", TOK_ACCEPT }, { "count", TOK_COUNT }, { "pipe", TOK_PIPE }, { "queue", TOK_QUEUE }, { "divert", TOK_DIVERT }, { "tee", TOK_TEE }, { "netgraph", TOK_NETGRAPH }, { "ngtee", TOK_NGTEE }, { "fwd", TOK_FORWARD }, { "forward", TOK_FORWARD }, { "skipto", TOK_SKIPTO }, { "deny", TOK_DENY }, { "drop", TOK_DENY }, { "reject", TOK_REJECT }, { "reset6", TOK_RESET6 }, { "reset", TOK_RESET }, { "unreach6", TOK_UNREACH6 }, { "unreach", TOK_UNREACH }, { "check-state", TOK_CHECKSTATE }, { "//", TOK_COMMENT }, { "nat", TOK_NAT }, { "reass", TOK_REASS }, { "setfib", TOK_SETFIB }, { "setdscp", TOK_SETDSCP }, { "call", TOK_CALL }, { "return", TOK_RETURN }, { "eaction", TOK_EACTION }, { "tcp-setmss", TOK_TCPSETMSS }, { NULL, 0 } /* terminator */ }; static struct _s_x rule_action_params[] = { { "altq", TOK_ALTQ }, { "log", TOK_LOG }, { "tag", TOK_TAG }, { "untag", TOK_UNTAG }, { NULL, 0 } /* terminator */ }; /* * The 'lookup' instruction accepts one of the following arguments. * -1 is a terminator for the list. * Arguments are passed as v[1] in O_DST_LOOKUP options. */ static int lookup_key[] = { TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT, TOK_UID, TOK_JAIL, TOK_DSCP, -1 }; static struct _s_x rule_options[] = { { "tagged", TOK_TAGGED }, { "uid", TOK_UID }, { "gid", TOK_GID }, { "jail", TOK_JAIL }, { "in", TOK_IN }, { "limit", TOK_LIMIT }, { "set-limit", TOK_SETLIMIT }, { "keep-state", TOK_KEEPSTATE }, { "record-state", TOK_RECORDSTATE }, { "bridged", TOK_LAYER2 }, { "layer2", TOK_LAYER2 }, { "out", TOK_OUT }, { "diverted", TOK_DIVERTED }, { "diverted-loopback", TOK_DIVERTEDLOOPBACK }, { "diverted-output", TOK_DIVERTEDOUTPUT }, { "xmit", TOK_XMIT }, { "recv", TOK_RECV }, { "via", TOK_VIA }, { "fragment", TOK_FRAG }, { "frag", TOK_FRAG }, { "fib", TOK_FIB }, { "ipoptions", TOK_IPOPTS }, { "ipopts", TOK_IPOPTS }, { "iplen", TOK_IPLEN }, { "ipid", TOK_IPID }, { "ipprecedence", TOK_IPPRECEDENCE }, { "dscp", TOK_DSCP }, { "iptos", TOK_IPTOS }, { "ipttl", TOK_IPTTL }, { "ipversion", TOK_IPVER }, { "ipver", TOK_IPVER }, { "estab", TOK_ESTAB }, { "established", TOK_ESTAB }, { "setup", TOK_SETUP }, { "sockarg", TOK_SOCKARG }, { "tcpdatalen", TOK_TCPDATALEN }, { "tcpflags", TOK_TCPFLAGS }, { "tcpflgs", TOK_TCPFLAGS }, { "tcpoptions", TOK_TCPOPTS }, { "tcpopts", TOK_TCPOPTS }, { "tcpseq", TOK_TCPSEQ }, { "tcpack", TOK_TCPACK }, { "tcpwin", TOK_TCPWIN }, { "icmptype", TOK_ICMPTYPES }, { "icmptypes", TOK_ICMPTYPES }, { "dst-ip", TOK_DSTIP }, { "src-ip", TOK_SRCIP }, { "dst-port", TOK_DSTPORT }, { "src-port", TOK_SRCPORT }, { "proto", TOK_PROTO }, { "MAC", TOK_MAC }, { "mac", TOK_MAC }, { "mac-type", TOK_MACTYPE }, { "verrevpath", TOK_VERREVPATH }, { "versrcreach", TOK_VERSRCREACH }, { "antispoof", TOK_ANTISPOOF }, { "ipsec", TOK_IPSEC }, { "icmp6type", TOK_ICMP6TYPES }, { "icmp6types", TOK_ICMP6TYPES }, { "ext6hdr", TOK_EXT6HDR}, { "flow-id", TOK_FLOWID}, { "ipv6", TOK_IPV6}, { "ip6", TOK_IPV6}, { "ipv4", TOK_IPV4}, { "ip4", TOK_IPV4}, { "dst-ipv6", TOK_DSTIP6}, { "dst-ip6", TOK_DSTIP6}, { "src-ipv6", TOK_SRCIP6}, { "src-ip6", TOK_SRCIP6}, { "lookup", TOK_LOOKUP}, { "flow", TOK_FLOW}, { "defer-action", TOK_SKIPACTION }, { "defer-immediate-action", TOK_SKIPACTION }, { "//", TOK_COMMENT }, { "not", TOK_NOT }, /* pseudo option */ { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ { "or", TOK_OR }, /* pseudo option */ { "|", /* escape */ TOK_OR }, /* pseudo option */ { "{", TOK_STARTBRACE }, /* pseudo option */ { "(", TOK_STARTBRACE }, /* pseudo option */ { "}", TOK_ENDBRACE }, /* pseudo option */ { ")", TOK_ENDBRACE }, /* pseudo option */ { NULL, 0 } /* terminator */ }; void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg); static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo, ipfw_cfg_lheader **pcfg, size_t *psize); static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, ipfw_cfg_lheader *cfg, size_t sz, int ac, char **av); static void ipfw_list_tifaces(void); struct tidx; static uint16_t pack_object(struct tidx *tstate, char *name, int otype); static uint16_t pack_table(struct tidx *tstate, char *name); static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx); static void object_sort_ctlv(ipfw_obj_ctlv *ctlv); static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type); /* * Simple string buffer API. * Used to simplify buffer passing between function and for * transparent overrun handling. */ /* * Allocates new buffer of given size @sz. * * Returns 0 on success. */ int bp_alloc(struct buf_pr *b, size_t size) { memset(b, 0, sizeof(struct buf_pr)); if ((b->buf = calloc(1, size)) == NULL) return (ENOMEM); b->ptr = b->buf; b->size = size; b->avail = b->size; return (0); } void bp_free(struct buf_pr *b) { free(b->buf); } /* * Flushes buffer so new writer start from beginning. */ void bp_flush(struct buf_pr *b) { b->ptr = b->buf; b->avail = b->size; b->buf[0] = '\0'; } /* * Print message specified by @format and args. * Automatically manage buffer space and transparently handle * buffer overruns. * * Returns number of bytes that should have been printed. */ int bprintf(struct buf_pr *b, char *format, ...) { va_list args; int i; va_start(args, format); i = vsnprintf(b->ptr, b->avail, format, args); va_end(args); if (i > b->avail || i < 0) { /* Overflow or print error */ b->avail = 0; } else { b->ptr += i; b->avail -= i; } b->needed += i; return (i); } /* * Special values printer for tablearg-aware opcodes. */ void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg) { if (str != NULL) bprintf(bp, "%s", str); if (arg == IP_FW_TARG) bprintf(bp, "tablearg"); else bprintf(bp, "%u", arg); } /* * Helper routine to print a possibly unaligned uint64_t on * various platform. If width > 0, print the value with * the desired width, followed by a space; * otherwise, return the required width. */ int pr_u64(struct buf_pr *b, uint64_t *pd, int width) { #ifdef TCC #define U64_FMT "I64" #else #define U64_FMT "llu" #endif uint64_t u; unsigned long long d; bcopy (pd, &u, sizeof(u)); d = u; return (width > 0) ? bprintf(b, "%*" U64_FMT " ", width, d) : snprintf(NULL, 0, "%" U64_FMT, d) ; #undef U64_FMT } void * safe_calloc(size_t number, size_t size) { void *ret = calloc(number, size); if (ret == NULL) err(EX_OSERR, "calloc"); return ret; } void * safe_realloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); if (ret == NULL) err(EX_OSERR, "realloc"); return ret; } /* * Compare things like interface or table names. */ int stringnum_cmp(const char *a, const char *b) { int la, lb; la = strlen(a); lb = strlen(b); if (la > lb) return (1); else if (la < lb) return (-01); return (strcmp(a, b)); } /* * conditionally runs the command. * Selected options or negative -> getsockopt */ int do_cmd(int optname, void *optval, uintptr_t optlen) { int i; if (co.test_only) return 0; if (ipfw_socket == -1) ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (ipfw_socket < 0) err(EX_UNAVAILABLE, "socket"); if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET || optname == IP_FW_ADD || optname == IP_FW3 || optname == IP_FW_NAT_GET_CONFIG || optname < 0 || optname == IP_FW_NAT_GET_LOG) { if (optname < 0) optname = -optname; i = getsockopt(ipfw_socket, IPPROTO_IP, optname, optval, (socklen_t *)optlen); } else { i = setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen); } return i; } /* * do_set3 - pass ipfw control cmd to kernel * @optname: option name * @optval: pointer to option data * @optlen: option length * * Assumes op3 header is already embedded. * Calls setsockopt() with IP_FW3 as kernel-visible opcode. * Returns 0 on success or errno otherwise. */ int do_set3(int optname, ip_fw3_opheader *op3, size_t optlen) { if (co.test_only) return (0); if (ipfw_socket == -1) ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (ipfw_socket < 0) err(EX_UNAVAILABLE, "socket"); op3->opcode = optname; return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen)); } /* * do_get3 - pass ipfw control cmd to kernel * @optname: option name * @optval: pointer to option data * @optlen: pointer to option length * * Assumes op3 header is already embedded. * Calls getsockopt() with IP_FW3 as kernel-visible opcode. * Returns 0 on success or errno otherwise. */ int do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen) { int error; socklen_t len; if (co.test_only) return (0); if (ipfw_socket == -1) ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (ipfw_socket < 0) err(EX_UNAVAILABLE, "socket"); op3->opcode = optname; len = *optlen; error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &len); *optlen = len; return (error); } /** * match_token takes a table and a string, returns the value associated * with the string (-1 in case of failure). */ int match_token(struct _s_x *table, const char *string) { struct _s_x *pt; uint i = strlen(string); for (pt = table ; i && pt->s != NULL ; pt++) if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) return pt->x; return (-1); } /** * match_token_relaxed takes a table and a string, returns the value associated * with the string for the best match. * * Returns: * value from @table for matched records * -1 for non-matched records * -2 if more than one records match @string. */ int match_token_relaxed(struct _s_x *table, const char *string) { struct _s_x *pt, *m; int i, c; i = strlen(string); c = 0; for (pt = table ; i != 0 && pt->s != NULL ; pt++) { if (strncmp(pt->s, string, i) != 0) continue; m = pt; c++; } if (c == 1) return (m->x); return (c > 0 ? -2: -1); } int get_token(struct _s_x *table, const char *string, const char *errbase) { int tcmd; if ((tcmd = match_token_relaxed(table, string)) < 0) errx(EX_USAGE, "%s %s %s", (tcmd == 0) ? "invalid" : "ambiguous", errbase, string); return (tcmd); } /** * match_value takes a table and a value, returns the string associated * with the value (NULL in case of failure). */ char const * match_value(struct _s_x *p, int value) { for (; p->s != NULL; p++) if (p->x == value) return p->s; return NULL; } size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter) { struct _s_x *pt; int l; size_t sz; for (sz = 0, pt = table ; pt->s != NULL; pt++) { l = snprintf(buf + sz, bufsize - sz, "%s%s", (sz == 0) ? "" : delimiter, pt->s); sz += l; bufsize += l; if (sz > bufsize) return (bufsize); } return (sz); } /* * helper function to process a set of flags and set bits in the * appropriate masks. */ int fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set, uint32_t *clear) { char *q; /* points to the separator */ int val; uint32_t *which; /* mask we are working on */ while (p && *p) { if (*p == '!') { p++; which = clear; } else which = set; q = strchr(p, ','); if (q) *q++ = '\0'; val = match_token(flags, p); if (val <= 0) { if (e != NULL) *e = p; return (-1); } *which |= (uint32_t)val; p = q; } return (0); } void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set) { char const *comma = ""; int i, l; for (i = 0; list[i].x != 0; i++) { if ((set & list[i].x) == 0) continue; set &= ~list[i].x; l = snprintf(buf, sz, "%s%s", comma, list[i].s); if (l >= sz) return; comma = ","; buf += l; sz -=l; } } /* * _substrcmp takes two strings and returns 1 if they do not match, * and 0 if they match exactly or the first string is a sub-string * of the second. A warning is printed to stderr in the case that the * first string is a sub-string of the second. * * This function will be removed in the future through the usual * deprecation process. */ int _substrcmp(const char *str1, const char* str2) { if (strncmp(str1, str2, strlen(str1)) != 0) return 1; if (strlen(str1) != strlen(str2)) warnx("DEPRECATED: '%s' matched '%s' as a sub-string", str1, str2); return 0; } /* * _substrcmp2 takes three strings and returns 1 if the first two do not match, * and 0 if they match exactly or the second string is a sub-string * of the first. A warning is printed to stderr in the case that the * first string does not match the third. * * This function exists to warn about the bizarre construction * strncmp(str, "by", 2) which is used to allow people to use a shortcut * for "bytes". The problem is that in addition to accepting "by", * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any * other string beginning with "by". * * This function will be removed in the future through the usual * deprecation process. */ int _substrcmp2(const char *str1, const char* str2, const char* str3) { if (strncmp(str1, str2, strlen(str2)) != 0) return 1; if (strcmp(str1, str3) != 0) warnx("DEPRECATED: '%s' matched '%s'", str1, str3); return 0; } /* * prints one port, symbolic or numeric */ static void print_port(struct buf_pr *bp, int proto, uint16_t port) { if (proto == IPPROTO_ETHERTYPE) { char const *s; if (co.do_resolv && (s = match_value(ether_types, port)) ) bprintf(bp, "%s", s); else bprintf(bp, "0x%04x", port); } else { struct servent *se = NULL; if (co.do_resolv) { struct protoent *pe = getprotobynumber(proto); se = getservbyport(htons(port), pe ? pe->p_name : NULL); } if (se) bprintf(bp, "%s", se->s_name); else bprintf(bp, "%d", port); } } static struct _s_x _port_name[] = { {"dst-port", O_IP_DSTPORT}, {"src-port", O_IP_SRCPORT}, {"ipid", O_IPID}, {"iplen", O_IPLEN}, {"ipttl", O_IPTTL}, {"mac-type", O_MAC_TYPE}, {"tcpdatalen", O_TCPDATALEN}, {"tcpwin", O_TCPWIN}, {"tagged", O_TAGGED}, {NULL, 0} }; /* * Print the values in a list 16-bit items of the types above. * XXX todo: add support for mask. */ static void print_newports(struct buf_pr *bp, ipfw_insn_u16 *cmd, int proto, int opcode) { uint16_t *p = cmd->ports; int i; char const *sep; if (opcode != 0) { sep = match_value(_port_name, opcode); if (sep == NULL) sep = "???"; bprintf(bp, " %s", sep); } sep = " "; for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { bprintf(bp, "%s", sep); print_port(bp, proto, p[0]); if (p[0] != p[1]) { bprintf(bp, "-"); print_port(bp, proto, p[1]); } sep = ","; } } /* * Like strtol, but also translates service names into port numbers * for some protocols. * In particular: * proto == -1 disables the protocol check; * proto == IPPROTO_ETHERTYPE looks up an internal table * proto == matches the values there. * Returns *end == s in case the parameter is not found. */ static int strtoport(char *s, char **end, int base, int proto) { char *p, *buf; char *s1; int i; *end = s; /* default - not found */ if (*s == '\0') return 0; /* not found */ if (isdigit(*s)) return strtol(s, end, base); /* * find separator. '\\' escapes the next char. */ for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++) if (*s1 == '\\' && s1[1] != '\0') s1++; buf = safe_calloc(s1 - s + 1, 1); /* * copy into a buffer skipping backslashes */ for (p = s, i = 0; p != s1 ; p++) if (*p != '\\') buf[i++] = *p; buf[i++] = '\0'; if (proto == IPPROTO_ETHERTYPE) { i = match_token(ether_types, buf); free(buf); if (i != -1) { /* found */ *end = s1; return i; } } else { struct protoent *pe = NULL; struct servent *se; if (proto != 0) pe = getprotobynumber(proto); setservent(1); se = getservbyname(buf, pe ? pe->p_name : NULL); free(buf); if (se != NULL) { *end = s1; return ntohs(se->s_port); } } return 0; /* not found */ } /* * Fill the body of the command with the list of port ranges. */ static int fill_newports(ipfw_insn_u16 *cmd, char *av, int proto, int cblen) { uint16_t a, b, *p = cmd->ports; int i = 0; char *s = av; while (*s) { a = strtoport(av, &s, 0, proto); if (s == av) /* empty or invalid argument */ return (0); CHECK_LENGTH(cblen, i + 2); switch (*s) { case '-': /* a range */ av = s + 1; b = strtoport(av, &s, 0, proto); /* Reject expressions like '1-abc' or '1-2-3'. */ if (s == av || (*s != ',' && *s != '\0')) return (0); p[0] = a; p[1] = b; break; case ',': /* comma separated list */ case '\0': p[0] = p[1] = a; break; default: warnx("port list: invalid separator <%c> in <%s>", *s, av); return (0); } i++; p += 2; av = s + 1; } if (i > 0) { if (i + 1 > F_LEN_MASK) errx(EX_DATAERR, "too many ports/ranges\n"); cmd->o.len |= i + 1; /* leave F_NOT and F_OR untouched */ } return (i); } /* * Fill the body of the command with the list of DiffServ codepoints. */ static void fill_dscp(ipfw_insn *cmd, char *av, int cblen) { uint32_t *low, *high; char *s = av, *a; int code; cmd->opcode = O_DSCP; cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1; CHECK_CMDLEN; low = (uint32_t *)(cmd + 1); high = low + 1; *low = 0; *high = 0; while (s != NULL) { a = strchr(s, ','); if (a != NULL) *a++ = '\0'; if (isalpha(*s)) { if ((code = match_token(f_ipdscp, s)) == -1) errx(EX_DATAERR, "Unknown DSCP code"); } else { code = strtoul(s, NULL, 10); if (code < 0 || code > 63) errx(EX_DATAERR, "Invalid DSCP value"); } if (code >= 32) *high |= 1 << (code - 32); else *low |= 1 << code; s = a; } } static struct _s_x icmpcodes[] = { { "net", ICMP_UNREACH_NET }, { "host", ICMP_UNREACH_HOST }, { "protocol", ICMP_UNREACH_PROTOCOL }, { "port", ICMP_UNREACH_PORT }, { "needfrag", ICMP_UNREACH_NEEDFRAG }, { "srcfail", ICMP_UNREACH_SRCFAIL }, { "net-unknown", ICMP_UNREACH_NET_UNKNOWN }, { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN }, { "isolated", ICMP_UNREACH_ISOLATED }, { "net-prohib", ICMP_UNREACH_NET_PROHIB }, { "host-prohib", ICMP_UNREACH_HOST_PROHIB }, { "tosnet", ICMP_UNREACH_TOSNET }, { "toshost", ICMP_UNREACH_TOSHOST }, { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB }, { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE }, { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF }, { NULL, 0 } }; static void fill_reject_code(u_short *codep, char *str) { int val; char *s; val = strtoul(str, &s, 0); if (s == str || *s != '\0' || val >= 0x100) val = match_token(icmpcodes, str); if (val < 0) errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); *codep = val; return; } static void print_reject_code(struct buf_pr *bp, uint16_t code) { char const *s; if ((s = match_value(icmpcodes, code)) != NULL) bprintf(bp, "unreach %s", s); else bprintf(bp, "unreach %u", code); } /* * Returns the number of bits set (from left) in a contiguous bitmask, * or -1 if the mask is not contiguous. * XXX this needs a proper fix. * This effectively works on masks in big-endian (network) format. * when compiled on little endian architectures. * * First bit is bit 7 of the first byte -- note, for MAC addresses, * the first bit on the wire is bit 0 of the first byte. * len is the max length in bits. */ int contigmask(uint8_t *p, int len) { int i, n; for (i=0; iarg1 & 0xff; uint8_t clear = (cmd->arg1 >> 8) & 0xff; if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) { bprintf(bp, " setup"); return; } bprintf(bp, " %s ", name); for (i=0; list[i].x != 0; i++) { if (set & list[i].x) { set &= ~list[i].x; bprintf(bp, "%s%s", comma, list[i].s); comma = ","; } if (clear & list[i].x) { clear &= ~list[i].x; bprintf(bp, "%s!%s", comma, list[i].s); comma = ","; } } } /* * Print the ip address contained in a command. */ static void print_ip(struct buf_pr *bp, const struct format_opts *fo, ipfw_insn_ip *cmd) { struct hostent *he = NULL; struct in_addr *ia; uint32_t len = F_LEN((ipfw_insn *)cmd); uint32_t *a = ((ipfw_insn_u32 *)cmd)->d; char *t; bprintf(bp, " "); if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) { uint32_t d = a[1]; const char *arg = ""; if (d < sizeof(lookup_key)/sizeof(lookup_key[0])) arg = match_value(rule_options, lookup_key[d]); t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1); bprintf(bp, "lookup %s %s", arg, t); return; } if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { bprintf(bp, "me"); return; } if (cmd->o.opcode == O_IP_SRC_LOOKUP || cmd->o.opcode == O_IP_DST_LOOKUP) { t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1); bprintf(bp, "table(%s", t); if (len == F_INSN_SIZE(ipfw_insn_u32)) bprintf(bp, ",%u", *a); bprintf(bp, ")"); return; } if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { uint32_t x, *map = (uint32_t *)&(cmd->mask); int i, j; char comma = '{'; x = cmd->o.arg1 - 1; x = htonl( ~x ); cmd->addr.s_addr = htonl(cmd->addr.s_addr); bprintf(bp, "%s/%d", inet_ntoa(cmd->addr), contigmask((uint8_t *)&x, 32)); x = cmd->addr.s_addr = htonl(cmd->addr.s_addr); x &= 0xff; /* base */ /* * Print bits and ranges. * Locate first bit set (i), then locate first bit unset (j). * If we have 3+ consecutive bits set, then print them as a * range, otherwise only print the initial bit and rescan. */ for (i=0; i < cmd->o.arg1; i++) if (map[i/32] & (1<<(i & 31))) { for (j=i+1; j < cmd->o.arg1; j++) if (!(map[ j/32] & (1<<(j & 31)))) break; bprintf(bp, "%c%d", comma, i+x); if (j>i+2) { /* range has at least 3 elements */ bprintf(bp, "-%d", j-1+x); i = j-1; } comma = ','; } bprintf(bp, "}"); return; } /* * len == 2 indicates a single IP, whereas lists of 1 or more * addr/mask pairs have len = (2n+1). We convert len to n so we * use that to count the number of entries. */ for (len = len / 2; len > 0; len--, a += 2) { int mb = /* mask length */ (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ? 32 : contigmask((uint8_t *)&(a[1]), 32); if (mb == 32 && co.do_resolv) - he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET); + he = gethostbyaddr((char *)&(a[0]), sizeof(in_addr_t), + AF_INET); if (he != NULL) /* resolved to name */ bprintf(bp, "%s", he->h_name); else if (mb == 0) /* any */ bprintf(bp, "any"); else { /* numeric IP followed by some kind of mask */ ia = (struct in_addr *)&a[0]; bprintf(bp, "%s", inet_ntoa(*ia)); if (mb < 0) { ia = (struct in_addr *)&a[1]; bprintf(bp, ":%s", inet_ntoa(*ia)); } else if (mb < 32) bprintf(bp, "/%d", mb); } if (len > 1) bprintf(bp, ","); } } /* * prints a MAC address/mask pair */ static void format_mac(struct buf_pr *bp, uint8_t *addr, uint8_t *mask) { int l = contigmask(mask, 48); if (l == 0) bprintf(bp, " any"); else { bprintf(bp, " %02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); if (l == -1) bprintf(bp, "&%02x:%02x:%02x:%02x:%02x:%02x", mask[0], mask[1], mask[2], mask[3], mask[4], mask[5]); else if (l < 48) bprintf(bp, "/%d", l); } } static void print_mac(struct buf_pr *bp, ipfw_insn_mac *mac) { bprintf(bp, " MAC"); format_mac(bp, mac->addr, mac->mask); format_mac(bp, mac->addr + 6, mac->mask + 6); } static void fill_icmptypes(ipfw_insn_u32 *cmd, char *av) { uint8_t type; cmd->d[0] = 0; while (*av) { if (*av == ',') av++; type = strtoul(av, &av, 0); if (*av != ',' && *av != '\0') errx(EX_DATAERR, "invalid ICMP type"); if (type > 31) errx(EX_DATAERR, "ICMP type out of range"); cmd->d[0] |= 1 << type; } cmd->o.opcode = O_ICMPTYPE; cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); } static void print_icmptypes(struct buf_pr *bp, ipfw_insn_u32 *cmd) { int i; char sep= ' '; bprintf(bp, " icmptypes"); for (i = 0; i < 32; i++) { if ( (cmd->d[0] & (1 << (i))) == 0) continue; bprintf(bp, "%c%d", sep, i); sep = ','; } } static void print_dscp(struct buf_pr *bp, ipfw_insn_u32 *cmd) { int i = 0; uint32_t *v; char sep= ' '; const char *code; bprintf(bp, " dscp"); v = cmd->d; while (i < 64) { if (*v & (1 << i)) { if ((code = match_value(f_ipdscp, i)) != NULL) bprintf(bp, "%c%s", sep, code); else bprintf(bp, "%c%d", sep, i); sep = ','; } if ((++i % 32) == 0) v++; } } #define insntod(cmd, type) ((ipfw_insn_ ## type *)(cmd)) struct show_state { struct ip_fw_rule *rule; const ipfw_insn *eaction; uint8_t *printed; int flags; #define HAVE_PROTO 0x0001 #define HAVE_SRCIP 0x0002 #define HAVE_DSTIP 0x0004 #define HAVE_PROBE_STATE 0x0008 int proto; int or_block; }; static int init_show_state(struct show_state *state, struct ip_fw_rule *rule) { state->printed = calloc(rule->cmd_len, sizeof(uint8_t)); if (state->printed == NULL) return (ENOMEM); state->rule = rule; state->eaction = NULL; state->flags = 0; state->proto = 0; state->or_block = 0; return (0); } static void free_show_state(struct show_state *state) { free(state->printed); } static uint8_t is_printed_opcode(struct show_state *state, const ipfw_insn *cmd) { return (state->printed[cmd - state->rule->cmd]); } static void mark_printed(struct show_state *state, const ipfw_insn *cmd) { state->printed[cmd - state->rule->cmd] = 1; } static void print_limit_mask(struct buf_pr *bp, const ipfw_insn_limit *limit) { struct _s_x *p = limit_masks; char const *comma = " "; uint8_t x; for (x = limit->limit_mask; p->x != 0; p++) { if ((x & p->x) == p->x) { x &= ~p->x; bprintf(bp, "%s%s", comma, p->s); comma = ","; } } bprint_uint_arg(bp, " ", limit->conn_limit); } static int print_instruction(struct buf_pr *bp, const struct format_opts *fo, struct show_state *state, ipfw_insn *cmd) { struct protoent *pe; struct passwd *pwd; struct group *grp; const char *s; double d; if (is_printed_opcode(state, cmd)) return (0); if ((cmd->len & F_OR) != 0 && state->or_block == 0) bprintf(bp, " {"); if (cmd->opcode != O_IN && (cmd->len & F_NOT) != 0) bprintf(bp, " not"); switch (cmd->opcode) { case O_PROB: d = 1.0 * insntod(cmd, u32)->d[0] / 0x7fffffff; bprintf(bp, "prob %f ", d); break; case O_PROBE_STATE: /* no need to print anything here */ state->flags |= HAVE_PROBE_STATE; break; case O_IP_SRC: case O_IP_SRC_LOOKUP: case O_IP_SRC_MASK: case O_IP_SRC_ME: case O_IP_SRC_SET: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip"); print_ip(bp, fo, insntod(cmd, ip)); break; case O_IP_DST: case O_IP_DST_LOOKUP: case O_IP_DST_MASK: case O_IP_DST_ME: case O_IP_DST_SET: if (state->flags & HAVE_DSTIP) bprintf(bp, " dst-ip"); print_ip(bp, fo, insntod(cmd, ip)); break; case O_IP6_SRC: case O_IP6_SRC_MASK: case O_IP6_SRC_ME: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip6"); print_ip6(bp, insntod(cmd, ip6)); break; case O_IP6_DST: case O_IP6_DST_MASK: case O_IP6_DST_ME: if (state->flags & HAVE_DSTIP) bprintf(bp, " dst-ip6"); print_ip6(bp, insntod(cmd, ip6)); break; case O_FLOW6ID: print_flow6id(bp, insntod(cmd, u32)); break; case O_IP_DSTPORT: case O_IP_SRCPORT: print_newports(bp, insntod(cmd, u16), state->proto, (state->flags & (HAVE_SRCIP | HAVE_DSTIP)) == (HAVE_SRCIP | HAVE_DSTIP) ? cmd->opcode: 0); break; case O_PROTO: pe = getprotobynumber(cmd->arg1); if (state->flags & HAVE_PROTO) bprintf(bp, " proto"); if (pe != NULL) bprintf(bp, " %s", pe->p_name); else bprintf(bp, " %u", cmd->arg1); + state->proto = cmd->arg1; break; case O_MACADDR2: print_mac(bp, insntod(cmd, mac)); break; case O_MAC_TYPE: print_newports(bp, insntod(cmd, u16), IPPROTO_ETHERTYPE, cmd->opcode); break; case O_FRAG: bprintf(bp, " frag"); break; case O_FIB: bprintf(bp, " fib %u", cmd->arg1); break; case O_SOCKARG: bprintf(bp, " sockarg"); break; case O_IN: bprintf(bp, cmd->len & F_NOT ? " out" : " in"); break; case O_DIVERTED: switch (cmd->arg1) { case 3: bprintf(bp, " diverted"); break; case 2: bprintf(bp, " diverted-output"); break; case 1: bprintf(bp, " diverted-loopback"); break; default: bprintf(bp, " diverted-?<%u>", cmd->arg1); break; } break; case O_LAYER2: bprintf(bp, " layer2"); break; case O_XMIT: case O_RECV: case O_VIA: if (cmd->opcode == O_XMIT) s = "xmit"; else if (cmd->opcode == O_RECV) s = "recv"; else /* if (cmd->opcode == O_VIA) */ s = "via"; switch (insntod(cmd, if)->name[0]) { case '\0': bprintf(bp, " %s %s", s, inet_ntoa(insntod(cmd, if)->p.ip)); break; case '\1': bprintf(bp, " %s table(%s)", s, table_search_ctlv(fo->tstate, insntod(cmd, if)->p.kidx)); break; default: bprintf(bp, " %s %s", s, insntod(cmd, if)->name); } break; case O_IP_FLOW_LOOKUP: s = table_search_ctlv(fo->tstate, cmd->arg1); bprintf(bp, " flow table(%s", s); if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) bprintf(bp, ",%u", insntod(cmd, u32)->d[0]); bprintf(bp, ")"); break; case O_IPID: case O_IPTTL: case O_IPLEN: case O_TCPDATALEN: case O_TCPWIN: if (F_LEN(cmd) == 1) { switch (cmd->opcode) { case O_IPID: s = "ipid"; break; case O_IPTTL: s = "ipttl"; break; case O_IPLEN: s = "iplen"; break; case O_TCPDATALEN: s = "tcpdatalen"; break; case O_TCPWIN: s = "tcpwin"; break; } bprintf(bp, " %s %u", s, cmd->arg1); } else print_newports(bp, insntod(cmd, u16), 0, cmd->opcode); break; case O_IPVER: bprintf(bp, " ipver %u", cmd->arg1); break; case O_IPPRECEDENCE: bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5); break; case O_DSCP: print_dscp(bp, insntod(cmd, u32)); break; case O_IPOPT: print_flags(bp, "ipoptions", cmd, f_ipopts); break; case O_IPTOS: print_flags(bp, "iptos", cmd, f_iptos); break; case O_ICMPTYPE: print_icmptypes(bp, insntod(cmd, u32)); break; case O_ESTAB: bprintf(bp, " established"); break; case O_TCPFLAGS: print_flags(bp, "tcpflags", cmd, f_tcpflags); break; case O_TCPOPTS: print_flags(bp, "tcpoptions", cmd, f_tcpopts); break; case O_TCPACK: bprintf(bp, " tcpack %d", ntohl(insntod(cmd, u32)->d[0])); break; case O_TCPSEQ: bprintf(bp, " tcpseq %d", ntohl(insntod(cmd, u32)->d[0])); break; case O_UID: pwd = getpwuid(insntod(cmd, u32)->d[0]); if (pwd != NULL) bprintf(bp, " uid %s", pwd->pw_name); else bprintf(bp, " uid %u", insntod(cmd, u32)->d[0]); break; case O_GID: grp = getgrgid(insntod(cmd, u32)->d[0]); if (grp != NULL) bprintf(bp, " gid %s", grp->gr_name); else bprintf(bp, " gid %u", insntod(cmd, u32)->d[0]); break; case O_JAIL: bprintf(bp, " jail %d", insntod(cmd, u32)->d[0]); break; case O_VERREVPATH: bprintf(bp, " verrevpath"); break; case O_VERSRCREACH: bprintf(bp, " versrcreach"); break; case O_ANTISPOOF: bprintf(bp, " antispoof"); break; case O_IPSEC: bprintf(bp, " ipsec"); break; case O_NOP: bprintf(bp, " // %s", (char *)(cmd + 1)); break; case O_KEEP_STATE: if (state->flags & HAVE_PROBE_STATE) bprintf(bp, " keep-state"); else bprintf(bp, " record-state"); bprintf(bp, " :%s", object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_STATE_NAME)); break; case O_LIMIT: if (state->flags & HAVE_PROBE_STATE) bprintf(bp, " limit"); else bprintf(bp, " set-limit"); print_limit_mask(bp, insntod(cmd, limit)); bprintf(bp, " :%s", object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_STATE_NAME)); break; case O_IP6: bprintf(bp, " ip6"); break; case O_IP4: bprintf(bp, " ip4"); break; case O_ICMP6TYPE: print_icmp6types(bp, insntod(cmd, u32)); break; case O_EXT_HDR: print_ext6hdr(bp, cmd); break; case O_TAGGED: if (F_LEN(cmd) == 1) bprint_uint_arg(bp, " tagged ", cmd->arg1); else print_newports(bp, insntod(cmd, u16), 0, O_TAGGED); break; case O_SKIP_ACTION: bprintf(bp, " defer-immediate-action"); break; default: bprintf(bp, " [opcode %d len %d]", cmd->opcode, cmd->len); } if (cmd->len & F_OR) { bprintf(bp, " or"); state->or_block = 1; } else if (state->or_block != 0) { bprintf(bp, " }"); state->or_block = 0; } mark_printed(state, cmd); return (1); } static ipfw_insn * print_opcode(struct buf_pr *bp, struct format_opts *fo, struct show_state *state, int opcode) { ipfw_insn *cmd; int l; for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { /* We use zero opcode to print the rest of options */ if (opcode >= 0 && cmd->opcode != opcode) continue; /* * Skip O_NOP, when we printing the rest * of options, it will be handled separately. */ if (cmd->opcode == O_NOP && opcode != O_NOP) continue; if (!print_instruction(bp, fo, state, cmd)) continue; return (cmd); } return (NULL); } static void print_fwd(struct buf_pr *bp, const ipfw_insn *cmd) { char buf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2]; ipfw_insn_sa6 *sa6; ipfw_insn_sa *sa; uint16_t port; if (cmd->opcode == O_FORWARD_IP) { sa = insntod(cmd, sa); port = sa->sa.sin_port; if (sa->sa.sin_addr.s_addr == INADDR_ANY) bprintf(bp, "fwd tablearg"); else bprintf(bp, "fwd %s", inet_ntoa(sa->sa.sin_addr)); } else { sa6 = insntod(cmd, sa6); port = sa6->sa.sin6_port; bprintf(bp, "fwd "); if (getnameinfo((const struct sockaddr *)&sa6->sa, sizeof(struct sockaddr_in6), buf, sizeof(buf), NULL, 0, NI_NUMERICHOST) == 0) bprintf(bp, "%s", buf); } if (port != 0) bprintf(bp, ",%u", port); } static int print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, struct show_state *state, const ipfw_insn *cmd) { const char *s; if (is_printed_opcode(state, cmd)) return (0); switch (cmd->opcode) { case O_CHECK_STATE: bprintf(bp, "check-state"); if (cmd->arg1 != 0) s = object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_STATE_NAME); else s = NULL; bprintf(bp, " :%s", s ? s: "any"); break; case O_ACCEPT: bprintf(bp, "allow"); break; case O_COUNT: bprintf(bp, "count"); break; case O_DENY: bprintf(bp, "deny"); break; case O_REJECT: if (cmd->arg1 == ICMP_REJECT_RST) bprintf(bp, "reset"); else if (cmd->arg1 == ICMP_REJECT_ABORT) bprintf(bp, "abort"); else if (cmd->arg1 == ICMP_UNREACH_HOST) bprintf(bp, "reject"); else print_reject_code(bp, cmd->arg1); break; case O_UNREACH6: if (cmd->arg1 == ICMP6_UNREACH_RST) bprintf(bp, "reset6"); else if (cmd->arg1 == ICMP6_UNREACH_ABORT) bprintf(bp, "abort6"); else print_unreach6_code(bp, cmd->arg1); break; case O_SKIPTO: bprint_uint_arg(bp, "skipto ", cmd->arg1); break; case O_PIPE: bprint_uint_arg(bp, "pipe ", cmd->arg1); break; case O_QUEUE: bprint_uint_arg(bp, "queue ", cmd->arg1); break; case O_DIVERT: bprint_uint_arg(bp, "divert ", cmd->arg1); break; case O_TEE: bprint_uint_arg(bp, "tee ", cmd->arg1); break; case O_NETGRAPH: bprint_uint_arg(bp, "netgraph ", cmd->arg1); break; case O_NGTEE: bprint_uint_arg(bp, "ngtee ", cmd->arg1); break; case O_FORWARD_IP: case O_FORWARD_IP6: print_fwd(bp, cmd); break; case O_LOG: if (insntod(cmd, log)->max_log > 0) bprintf(bp, " log logamount %d", insntod(cmd, log)->max_log); else bprintf(bp, " log"); break; case O_ALTQ: #ifndef NO_ALTQ print_altq_cmd(bp, insntod(cmd, altq)); #endif break; case O_TAG: bprint_uint_arg(bp, cmd->len & F_NOT ? " untag ": " tag ", cmd->arg1); break; case O_NAT: if (cmd->arg1 != IP_FW_NAT44_GLOBAL) bprint_uint_arg(bp, "nat ", cmd->arg1); else bprintf(bp, "nat global"); break; case O_SETFIB: if (cmd->arg1 == IP_FW_TARG) bprint_uint_arg(bp, "setfib ", cmd->arg1); else bprintf(bp, "setfib %u", cmd->arg1 & 0x7FFF); break; case O_EXTERNAL_ACTION: /* * The external action can consists of two following * each other opcodes - O_EXTERNAL_ACTION and * O_EXTERNAL_INSTANCE. The first contains the ID of * name of external action. The second contains the ID * of name of external action instance. * NOTE: in case when external action has no named * instances support, the second opcode isn't needed. */ state->eaction = cmd; s = object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_EACTION); if (match_token(rule_eactions, s) != -1) bprintf(bp, "%s", s); else bprintf(bp, "eaction %s", s); break; case O_EXTERNAL_INSTANCE: if (state->eaction == NULL) break; /* * XXX: we need to teach ipfw(9) to rewrite opcodes * in the user buffer on rule addition. When we add * the rule, we specify zero TLV type for * O_EXTERNAL_INSTANCE object. To show correct * rule after `ipfw add` we need to search instance * name with zero type. But when we do `ipfw show` * we calculate TLV type using IPFW_TLV_EACTION_NAME() * macro. */ s = object_search_ctlv(fo->tstate, cmd->arg1, 0); if (s == NULL) s = object_search_ctlv(fo->tstate, cmd->arg1, IPFW_TLV_EACTION_NAME( state->eaction->arg1)); bprintf(bp, " %s", s); break; case O_EXTERNAL_DATA: if (state->eaction == NULL) break; /* * Currently we support data formatting only for * external data with datalen u16. For unknown data * print its size in bytes. */ if (cmd->len == F_INSN_SIZE(ipfw_insn)) bprintf(bp, " %u", cmd->arg1); else bprintf(bp, " %ubytes", cmd->len * sizeof(uint32_t)); break; case O_SETDSCP: if (cmd->arg1 == IP_FW_TARG) { bprintf(bp, "setdscp tablearg"); break; } s = match_value(f_ipdscp, cmd->arg1 & 0x3F); if (s != NULL) bprintf(bp, "setdscp %s", s); else bprintf(bp, "setdscp %u", cmd->arg1 & 0x3F); break; case O_REASS: bprintf(bp, "reass"); break; case O_CALLRETURN: if (cmd->len & F_NOT) bprintf(bp, "return"); else bprint_uint_arg(bp, "call ", cmd->arg1); break; default: bprintf(bp, "** unrecognized action %d len %d ", cmd->opcode, cmd->len); } mark_printed(state, cmd); return (1); } static ipfw_insn * print_action(struct buf_pr *bp, struct format_opts *fo, struct show_state *state, uint8_t opcode) { ipfw_insn *cmd; int l; for (l = state->rule->cmd_len - state->rule->act_ofs, cmd = ACTION_PTR(state->rule); l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { if (cmd->opcode != opcode) continue; if (!print_action_instruction(bp, fo, state, cmd)) continue; return (cmd); } return (NULL); } static void print_proto(struct buf_pr *bp, struct format_opts *fo, struct show_state *state) { ipfw_insn *cmd; - int l, proto, ip4, ip6, tmp; + int l, proto, ip4, ip6; /* Count all O_PROTO, O_IP4, O_IP6 instructions. */ - proto = tmp = ip4 = ip6 = 0; + proto = ip4 = ip6 = 0; for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { switch (cmd->opcode) { case O_PROTO: proto++; break; case O_IP4: ip4 = 1; if (cmd->len & F_OR) ip4++; break; case O_IP6: ip6 = 1; if (cmd->len & F_OR) ip6++; break; default: continue; } } if (proto == 0 && ip4 == 0 && ip6 == 0) { state->proto = IPPROTO_IP; state->flags |= HAVE_PROTO; bprintf(bp, " ip"); return; } /* To handle the case { ip4 or ip6 }, print opcode with F_OR first */ cmd = NULL; if (ip4 || ip6) cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP4: O_IP6); if (cmd != NULL && (cmd->len & F_OR)) cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP6: O_IP4); if (cmd == NULL || (cmd->len & F_OR)) for (l = proto; l > 0; l--) { cmd = print_opcode(bp, fo, state, O_PROTO); - if (cmd != NULL && (cmd->len & F_OR) == 0) + if (cmd == NULL || (cmd->len & F_OR) == 0) break; - tmp = cmd->arg1; } /* Initialize proto, it is used by print_newports() */ - if (tmp != 0) - state->proto = tmp; - else if (ip6 != 0) - state->proto = IPPROTO_IPV6; - else - state->proto = IPPROTO_IP; state->flags |= HAVE_PROTO; + if (state->proto == 0 && ip6 != 0) + state->proto = IPPROTO_IPV6; } static int match_opcode(int opcode, const int opcodes[], size_t nops) { int i; for (i = 0; i < nops; i++) if (opcode == opcodes[i]) return (1); return (0); } static void print_address(struct buf_pr *bp, struct format_opts *fo, struct show_state *state, const int opcodes[], size_t nops, int portop, int flag) { ipfw_insn *cmd; int count, l, portcnt, pf; count = portcnt = 0; for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { if (match_opcode(cmd->opcode, opcodes, nops)) count++; else if (cmd->opcode == portop) portcnt++; } if (count == 0) bprintf(bp, " any"); for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0 && count > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { if (!match_opcode(cmd->opcode, opcodes, nops)) continue; print_instruction(bp, fo, state, cmd); if ((cmd->len & F_OR) == 0) break; count--; } /* * If several O_IP_?PORT opcodes specified, leave them to the * options section. */ if (portcnt == 1) { for (l = state->rule->act_ofs, cmd = state->rule->cmd, pf = 0; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { if (cmd->opcode != portop) { pf = (cmd->len & F_OR); continue; } /* Print opcode iff it is not in OR block. */ if (pf == 0 && (cmd->len & F_OR) == 0) print_instruction(bp, fo, state, cmd); break; } } state->flags |= flag; } static const int action_opcodes[] = { O_CHECK_STATE, O_ACCEPT, O_COUNT, O_DENY, O_REJECT, O_UNREACH6, O_SKIPTO, O_PIPE, O_QUEUE, O_DIVERT, O_TEE, O_NETGRAPH, O_NGTEE, O_FORWARD_IP, O_FORWARD_IP6, O_NAT, O_SETFIB, O_SETDSCP, O_REASS, O_CALLRETURN, /* keep the following opcodes at the end of the list */ O_EXTERNAL_ACTION, O_EXTERNAL_INSTANCE, O_EXTERNAL_DATA }; static const int modifier_opcodes[] = { O_LOG, O_ALTQ, O_TAG }; static const int src_opcodes[] = { O_IP_SRC, O_IP_SRC_LOOKUP, O_IP_SRC_MASK, O_IP_SRC_ME, O_IP_SRC_SET, O_IP6_SRC, O_IP6_SRC_MASK, O_IP6_SRC_ME }; static const int dst_opcodes[] = { O_IP_DST, O_IP_DST_LOOKUP, O_IP_DST_MASK, O_IP_DST_ME, O_IP_DST_SET, O_IP6_DST, O_IP6_DST_MASK, O_IP6_DST_ME }; static void show_static_rule(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr) { struct show_state state; ipfw_insn *cmd; static int twidth = 0; int i; /* Print # DISABLED or skip the rule */ if ((fo->set_mask & (1 << rule->set)) == 0) { /* disabled mask */ if (!co->show_sets) return; else bprintf(bp, "# DISABLED "); } if (init_show_state(&state, rule) != 0) { warn("init_show_state() failed"); return; } bprintf(bp, "%05u ", rule->rulenum); /* Print counters if enabled */ if (fo->pcwidth > 0 || fo->bcwidth > 0) { pr_u64(bp, &cntr->pcnt, fo->pcwidth); pr_u64(bp, &cntr->bcnt, fo->bcwidth); } /* Print timestamp */ if (co->do_time == TIMESTAMP_NUMERIC) bprintf(bp, "%10u ", cntr->timestamp); else if (co->do_time == TIMESTAMP_STRING) { char timestr[30]; time_t t = (time_t)0; if (twidth == 0) { strcpy(timestr, ctime(&t)); *strchr(timestr, '\n') = '\0'; twidth = strlen(timestr); } if (cntr->timestamp > 0) { t = _long_to_time(cntr->timestamp); strcpy(timestr, ctime(&t)); *strchr(timestr, '\n') = '\0'; bprintf(bp, "%s ", timestr); } else { bprintf(bp, "%*s", twidth, " "); } } /* Print set number */ if (co->show_sets) bprintf(bp, "set %d ", rule->set); /* Print the optional "match probability" */ cmd = print_opcode(bp, fo, &state, O_PROB); /* Print rule action */ for (i = 0; i < nitems(action_opcodes); i++) { cmd = print_action(bp, fo, &state, action_opcodes[i]); if (cmd == NULL) continue; /* Handle special cases */ switch (cmd->opcode) { case O_CHECK_STATE: goto end; case O_EXTERNAL_ACTION: case O_EXTERNAL_INSTANCE: /* External action can have several instructions */ continue; } break; } /* Print rule modifiers */ for (i = 0; i < nitems(modifier_opcodes); i++) print_action(bp, fo, &state, modifier_opcodes[i]); /* * Print rule body */ if (co->comment_only != 0) goto end; if (rule->flags & IPFW_RULE_JUSTOPTS) { state.flags |= HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP; goto justopts; } print_proto(bp, fo, &state); /* Print source */ bprintf(bp, " from"); print_address(bp, fo, &state, src_opcodes, nitems(src_opcodes), O_IP_SRCPORT, HAVE_SRCIP); /* Print destination */ bprintf(bp, " to"); print_address(bp, fo, &state, dst_opcodes, nitems(dst_opcodes), O_IP_DSTPORT, HAVE_DSTIP); justopts: /* Print the rest of options */ while (print_opcode(bp, fo, &state, -1)) ; end: /* Print comment at the end */ cmd = print_opcode(bp, fo, &state, O_NOP); if (co->comment_only != 0 && cmd == NULL) bprintf(bp, " // ..."); bprintf(bp, "\n"); free_show_state(&state); } static void show_dyn_state(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, ipfw_dyn_rule *d) { struct protoent *pe; struct in_addr a; uint16_t rulenum; char buf[INET6_ADDRSTRLEN]; if (d->expire == 0 && d->dyn_type != O_LIMIT_PARENT) return; bcopy(&d->rule, &rulenum, sizeof(rulenum)); bprintf(bp, "%05d", rulenum); if (fo->pcwidth > 0 || fo->bcwidth > 0) { bprintf(bp, " "); pr_u64(bp, &d->pcnt, fo->pcwidth); pr_u64(bp, &d->bcnt, fo->bcwidth); bprintf(bp, "(%ds)", d->expire); } switch (d->dyn_type) { case O_LIMIT_PARENT: bprintf(bp, " PARENT %d", d->count); break; case O_LIMIT: bprintf(bp, " LIMIT"); break; case O_KEEP_STATE: /* bidir, no mask */ bprintf(bp, " STATE"); break; } if ((pe = getprotobynumber(d->id.proto)) != NULL) bprintf(bp, " %s", pe->p_name); else bprintf(bp, " proto %u", d->id.proto); if (d->id.addr_type == 4) { a.s_addr = htonl(d->id.src_ip); bprintf(bp, " %s %d", inet_ntoa(a), d->id.src_port); a.s_addr = htonl(d->id.dst_ip); bprintf(bp, " <-> %s %d", inet_ntoa(a), d->id.dst_port); } else if (d->id.addr_type == 6) { bprintf(bp, " %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf, sizeof(buf)), d->id.src_port); bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf, sizeof(buf)), d->id.dst_port); } else bprintf(bp, " UNKNOWN <-> UNKNOWN"); if (d->kidx != 0) bprintf(bp, " :%s", object_search_ctlv(fo->tstate, d->kidx, IPFW_TLV_STATE_NAME)); #define BOTH_SYN (TH_SYN | (TH_SYN << 8)) #define BOTH_FIN (TH_FIN | (TH_FIN << 8)) if (co->verbose) { bprintf(bp, " state 0x%08x%s", d->state, d->state ? " ": ","); if (d->state & IPFW_DYN_ORPHANED) bprintf(bp, "ORPHANED,"); if ((d->state & BOTH_SYN) == BOTH_SYN) bprintf(bp, "BOTH_SYN,"); else { if (d->state & TH_SYN) bprintf(bp, "F_SYN,"); if (d->state & (TH_SYN << 8)) bprintf(bp, "R_SYN,"); } if ((d->state & BOTH_FIN) == BOTH_FIN) bprintf(bp, "BOTH_FIN,"); else { if (d->state & TH_FIN) bprintf(bp, "F_FIN,"); if (d->state & (TH_FIN << 8)) bprintf(bp, "R_FIN,"); } bprintf(bp, " f_ack 0x%x, r_ack 0x%x", d->ack_fwd, d->ack_rev); } } static int do_range_cmd(int cmd, ipfw_range_tlv *rt) { ipfw_range_header rh; size_t sz; memset(&rh, 0, sizeof(rh)); memcpy(&rh.range, rt, sizeof(*rt)); rh.range.head.length = sizeof(*rt); rh.range.head.type = IPFW_TLV_RANGE; sz = sizeof(rh); if (do_get3(cmd, &rh.opheader, &sz) != 0) return (-1); /* Save number of matched objects */ rt->new_set = rh.range.new_set; return (0); } /* * This one handles all set-related commands * ipfw set { show | enable | disable } * ipfw set swap X Y * ipfw set move X to Y * ipfw set move rule X to Y */ void ipfw_sets_handler(char *av[]) { ipfw_range_tlv rt; char *msg; size_t size; uint32_t masks[2]; int i; uint16_t rulenum; uint8_t cmd; av++; memset(&rt, 0, sizeof(rt)); if (av[0] == NULL) errx(EX_USAGE, "set needs command"); if (_substrcmp(*av, "show") == 0) { struct format_opts fo; ipfw_cfg_lheader *cfg; memset(&fo, 0, sizeof(fo)); if (ipfw_get_config(&co, &fo, &cfg, &size) != 0) err(EX_OSERR, "requesting config failed"); for (i = 0, msg = "disable"; i < RESVD_SET; i++) if ((cfg->set_mask & (1<set_mask != (uint32_t)-1) ? " enable" : "enable"; for (i = 0; i < RESVD_SET; i++) if ((cfg->set_mask & (1< RESVD_SET) errx(EX_DATAERR, "invalid set number %s\n", av[0]); if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET) errx(EX_DATAERR, "invalid set number %s\n", av[1]); i = do_range_cmd(IP_FW_SET_SWAP, &rt); } else if (_substrcmp(*av, "move") == 0) { av++; if (av[0] && _substrcmp(*av, "rule") == 0) { rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */ cmd = IP_FW_XMOVE; av++; } else cmd = IP_FW_SET_MOVE; /* Move set to new one */ if (av[0] == NULL || av[1] == NULL || av[2] == NULL || av[3] != NULL || _substrcmp(av[1], "to") != 0) errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); rulenum = atoi(av[0]); rt.new_set = atoi(av[2]); if (cmd == IP_FW_XMOVE) { rt.start_rule = rulenum; rt.end_rule = rulenum; } else rt.set = rulenum; rt.new_set = atoi(av[2]); if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) || (cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) ) errx(EX_DATAERR, "invalid source number %s\n", av[0]); if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET) errx(EX_DATAERR, "invalid dest. set %s\n", av[1]); i = do_range_cmd(cmd, &rt); if (i < 0) err(EX_OSERR, "failed to move %s", cmd == IP_FW_SET_MOVE ? "set": "rule"); } else if (_substrcmp(*av, "disable") == 0 || _substrcmp(*av, "enable") == 0 ) { int which = _substrcmp(*av, "enable") == 0 ? 1 : 0; av++; masks[0] = masks[1] = 0; while (av[0]) { if (isdigit(**av)) { i = atoi(*av); if (i < 0 || i > RESVD_SET) errx(EX_DATAERR, "invalid set number %d\n", i); masks[which] |= (1<dcnt++; if (fo->show_counters == 0) return; if (co->use_set) { /* skip states from another set */ bcopy((char *)&d->rule + sizeof(uint16_t), &set, sizeof(uint8_t)); if (set != co->use_set - 1) return; } width = pr_u64(NULL, &d->pcnt, 0); if (width > fo->pcwidth) fo->pcwidth = width; width = pr_u64(NULL, &d->bcnt, 0); if (width > fo->bcwidth) fo->bcwidth = width; } static int foreach_state(struct cmdline_opts *co, struct format_opts *fo, caddr_t base, size_t sz, state_cb dyn_bc, void *dyn_arg) { int ttype; state_cb *fptr; void *farg; ipfw_obj_tlv *tlv; ipfw_obj_ctlv *ctlv; fptr = NULL; ttype = 0; while (sz > 0) { ctlv = (ipfw_obj_ctlv *)base; switch (ctlv->head.type) { case IPFW_TLV_DYNSTATE_LIST: base += sizeof(*ctlv); sz -= sizeof(*ctlv); ttype = IPFW_TLV_DYN_ENT; fptr = dyn_bc; farg = dyn_arg; break; default: return (sz); } while (sz > 0) { tlv = (ipfw_obj_tlv *)base; if (tlv->type != ttype) break; fptr(co, fo, farg, tlv + 1); sz -= tlv->length; base += tlv->length; } } return (sz); } static void prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo, ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz) { int bcwidth, pcwidth, width; int n; struct ip_fw_bcounter *cntr; struct ip_fw_rule *r; bcwidth = 0; pcwidth = 0; if (fo->show_counters != 0) { for (n = 0; n < rcnt; n++, rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { cntr = (struct ip_fw_bcounter *)(rtlv + 1); r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size); /* skip rules from another set */ if (co->use_set && r->set != co->use_set - 1) continue; /* packet counter */ width = pr_u64(NULL, &cntr->pcnt, 0); if (width > pcwidth) pcwidth = width; /* byte counter */ width = pr_u64(NULL, &cntr->bcnt, 0); if (width > bcwidth) bcwidth = width; } } fo->bcwidth = bcwidth; fo->pcwidth = pcwidth; fo->dcnt = 0; if (co->do_dynamic && dynsz > 0) foreach_state(co, fo, dynbase, dynsz, prepare_format_dyn, NULL); } static int list_static_range(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, ipfw_obj_tlv *rtlv, int rcnt) { int n, seen; struct ip_fw_rule *r; struct ip_fw_bcounter *cntr; int c = 0; for (n = seen = 0; n < rcnt; n++, rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { if ((fo->show_counters | fo->show_time) != 0) { cntr = (struct ip_fw_bcounter *)(rtlv + 1); r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size); } else { cntr = NULL; r = (struct ip_fw_rule *)(rtlv + 1); } if (r->rulenum > fo->last) break; if (co->use_set && r->set != co->use_set - 1) continue; if (r->rulenum >= fo->first && r->rulenum <= fo->last) { show_static_rule(co, fo, bp, r, cntr); printf("%s", bp->buf); c += rtlv->length; bp_flush(bp); seen++; } } return (seen); } static void list_dyn_state(struct cmdline_opts *co, struct format_opts *fo, void *_arg, void *_state) { uint16_t rulenum; uint8_t set; ipfw_dyn_rule *d; struct buf_pr *bp; d = (ipfw_dyn_rule *)_state; bp = (struct buf_pr *)_arg; bcopy(&d->rule, &rulenum, sizeof(rulenum)); if (rulenum > fo->last) return; if (co->use_set) { bcopy((char *)&d->rule + sizeof(uint16_t), &set, sizeof(uint8_t)); if (set != co->use_set - 1) return; } if (rulenum >= fo->first) { show_dyn_state(co, fo, bp, d); printf("%s\n", bp->buf); bp_flush(bp); } } static int list_dyn_range(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, caddr_t base, size_t sz) { sz = foreach_state(co, fo, base, sz, list_dyn_state, bp); return (sz); } void ipfw_list(int ac, char *av[], int show_counters) { ipfw_cfg_lheader *cfg; struct format_opts sfo; size_t sz; int error; int lac; char **lav; uint32_t rnum; char *endptr; if (co.test_only) { fprintf(stderr, "Testing only, list disabled\n"); return; } if (co.do_pipe) { dummynet_list(ac, av, show_counters); return; } ac--; av++; memset(&sfo, 0, sizeof(sfo)); /* Determine rule range to request */ if (ac > 0) { for (lac = ac, lav = av; lac != 0; lac--) { rnum = strtoul(*lav++, &endptr, 10); if (sfo.first == 0 || rnum < sfo.first) sfo.first = rnum; if (*endptr == '-') rnum = strtoul(endptr + 1, &endptr, 10); if (sfo.last == 0 || rnum > sfo.last) sfo.last = rnum; } } /* get configuraion from kernel */ cfg = NULL; sfo.show_counters = show_counters; sfo.show_time = co.do_time; if (co.do_dynamic != 2) sfo.flags |= IPFW_CFG_GET_STATIC; if (co.do_dynamic != 0) sfo.flags |= IPFW_CFG_GET_STATES; if ((sfo.show_counters | sfo.show_time) != 0) sfo.flags |= IPFW_CFG_GET_COUNTERS; if (ipfw_get_config(&co, &sfo, &cfg, &sz) != 0) err(EX_OSERR, "retrieving config failed"); error = ipfw_show_config(&co, &sfo, cfg, sz, ac, av); free(cfg); if (error != EX_OK) exit(error); } static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[]) { caddr_t dynbase; size_t dynsz; int rcnt; int exitval = EX_OK; int lac; char **lav; char *endptr; size_t readsz; struct buf_pr bp; ipfw_obj_ctlv *ctlv, *tstate; ipfw_obj_tlv *rbase; /* * Handle tablenames TLV first, if any */ tstate = NULL; rbase = NULL; dynbase = NULL; dynsz = 0; readsz = sizeof(*cfg); rcnt = 0; fo->set_mask = cfg->set_mask; ctlv = (ipfw_obj_ctlv *)(cfg + 1); if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { object_sort_ctlv(ctlv); fo->tstate = ctlv; readsz += ctlv->head.length; ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); } if (cfg->flags & IPFW_CFG_GET_STATIC) { /* We've requested static rules */ if (ctlv->head.type == IPFW_TLV_RULE_LIST) { rbase = (ipfw_obj_tlv *)(ctlv + 1); rcnt = ctlv->count; readsz += ctlv->head.length; ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); } } if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != sz)) { /* We may have some dynamic states */ dynsz = sz - readsz; /* Skip empty header */ if (dynsz != sizeof(ipfw_obj_ctlv)) dynbase = (caddr_t)ctlv; else dynsz = 0; } prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz); bp_alloc(&bp, 4096); /* if no rule numbers were specified, list all rules */ if (ac == 0) { fo->first = 0; fo->last = IPFW_DEFAULT_RULE; if (cfg->flags & IPFW_CFG_GET_STATIC) list_static_range(co, fo, &bp, rbase, rcnt); if (co->do_dynamic && dynsz > 0) { printf("## Dynamic rules (%d %zu):\n", fo->dcnt, dynsz); list_dyn_range(co, fo, &bp, dynbase, dynsz); } bp_free(&bp); return (EX_OK); } /* display specific rules requested on command line */ for (lac = ac, lav = av; lac != 0; lac--) { /* convert command line rule # */ fo->last = fo->first = strtoul(*lav++, &endptr, 10); if (*endptr == '-') fo->last = strtoul(endptr + 1, &endptr, 10); if (*endptr) { exitval = EX_USAGE; warnx("invalid rule number: %s", *(lav - 1)); continue; } if ((cfg->flags & IPFW_CFG_GET_STATIC) == 0) continue; if (list_static_range(co, fo, &bp, rbase, rcnt) == 0) { /* give precedence to other error(s) */ if (exitval == EX_OK) exitval = EX_UNAVAILABLE; if (fo->first == fo->last) warnx("rule %u does not exist", fo->first); else warnx("no rules in range %u-%u", fo->first, fo->last); } } if (co->do_dynamic && dynsz > 0) { printf("## Dynamic rules:\n"); for (lac = ac, lav = av; lac != 0; lac--) { fo->last = fo->first = strtoul(*lav++, &endptr, 10); if (*endptr == '-') fo->last = strtoul(endptr+1, &endptr, 10); if (*endptr) /* already warned */ continue; list_dyn_range(co, fo, &bp, dynbase, dynsz); } } bp_free(&bp); return (exitval); } /* * Retrieves current ipfw configuration of given type * and stores its pointer to @pcfg. * * Caller is responsible for freeing @pcfg. * * Returns 0 on success. */ static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo, ipfw_cfg_lheader **pcfg, size_t *psize) { ipfw_cfg_lheader *cfg; size_t sz; int i; if (co->test_only != 0) { fprintf(stderr, "Testing only, list disabled\n"); return (0); } /* Start with some data size */ sz = 4096; cfg = NULL; for (i = 0; i < 16; i++) { if (cfg != NULL) free(cfg); if ((cfg = calloc(1, sz)) == NULL) return (ENOMEM); cfg->flags = fo->flags; cfg->start_rule = fo->first; cfg->end_rule = fo->last; if (do_get3(IP_FW_XGET, &cfg->opheader, &sz) != 0) { if (errno != ENOMEM) { free(cfg); return (errno); } /* Buffer size is not enough. Try to increase */ sz = sz * 2; if (sz < cfg->size) sz = cfg->size; continue; } *pcfg = cfg; *psize = sz; return (0); } free(cfg); return (ENOMEM); } static int lookup_host (char *host, struct in_addr *ipaddr) { struct hostent *he; if (!inet_aton(host, ipaddr)) { if ((he = gethostbyname(host)) == NULL) return(-1); *ipaddr = *(struct in_addr *)he->h_addr_list[0]; } return(0); } struct tidx { ipfw_obj_ntlv *idx; uint32_t count; uint32_t size; uint16_t counter; uint8_t set; }; int ipfw_check_object_name(const char *name) { int c, i, l; /* * Check that name is null-terminated and contains * valid symbols only. Valid mask is: * [a-zA-Z0-9\-_\.]{1,63} */ l = strlen(name); if (l == 0 || l >= 64) return (EINVAL); for (i = 0; i < l; i++) { c = name[i]; if (isalpha(c) || isdigit(c) || c == '_' || c == '-' || c == '.') continue; return (EINVAL); } return (0); } static char *default_state_name = "default"; static int state_check_name(const char *name) { if (ipfw_check_object_name(name) != 0) return (EINVAL); if (strcmp(name, "any") == 0) return (EINVAL); return (0); } static int eaction_check_name(const char *name) { if (ipfw_check_object_name(name) != 0) return (EINVAL); /* Restrict some 'special' names */ if (match_token(rule_actions, name) != -1 && match_token(rule_action_params, name) != -1) return (EINVAL); return (0); } static uint16_t pack_object(struct tidx *tstate, char *name, int otype) { int i; ipfw_obj_ntlv *ntlv; for (i = 0; i < tstate->count; i++) { if (strcmp(tstate->idx[i].name, name) != 0) continue; if (tstate->idx[i].set != tstate->set) continue; if (tstate->idx[i].head.type != otype) continue; return (tstate->idx[i].idx); } if (tstate->count + 1 > tstate->size) { tstate->size += 4; tstate->idx = realloc(tstate->idx, tstate->size * sizeof(ipfw_obj_ntlv)); if (tstate->idx == NULL) return (0); } ntlv = &tstate->idx[i]; memset(ntlv, 0, sizeof(ipfw_obj_ntlv)); strlcpy(ntlv->name, name, sizeof(ntlv->name)); ntlv->head.type = otype; ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->set = tstate->set; ntlv->idx = ++tstate->counter; tstate->count++; return (ntlv->idx); } static uint16_t pack_table(struct tidx *tstate, char *name) { if (table_check_name(name) != 0) return (0); return (pack_object(tstate, name, IPFW_TLV_TBL_NAME)); } void fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate) { uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; uint16_t uidx; char *p; if ((p = strchr(av + 6, ')')) == NULL) errx(EX_DATAERR, "forgotten parenthesis: '%s'", av); *p = '\0'; p = strchr(av + 6, ','); if (p) *p++ = '\0'; if ((uidx = pack_table(tstate, av + 6)) == 0) errx(EX_DATAERR, "Invalid table name: %s", av + 6); cmd->opcode = opcode; cmd->arg1 = uidx; if (p) { cmd->len |= F_INSN_SIZE(ipfw_insn_u32); d[0] = strtoul(p, NULL, 0); } else cmd->len |= F_INSN_SIZE(ipfw_insn); } /* * fills the addr and mask fields in the instruction as appropriate from av. * Update length as appropriate. * The following formats are allowed: * me returns O_IP_*_ME * 1.2.3.4 single IP address * 1.2.3.4:5.6.7.8 address:mask * 1.2.3.4/24 address/mask * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet * We can have multiple comma-separated address/mask entries. */ static void fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate) { int len = 0; uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; cmd->o.len &= ~F_LEN_MASK; /* zero len */ if (_substrcmp(av, "any") == 0) return; if (_substrcmp(av, "me") == 0) { cmd->o.len |= F_INSN_SIZE(ipfw_insn); return; } if (strncmp(av, "table(", 6) == 0) { fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate); return; } while (av) { /* * After the address we can have '/' or ':' indicating a mask, * ',' indicating another address follows, '{' indicating a * set of addresses of unspecified size. */ char *t = NULL, *p = strpbrk(av, "/:,{"); int masklen; char md, nd = '\0'; CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn) + 2 + len); if (p) { md = *p; *p++ = '\0'; if ((t = strpbrk(p, ",{")) != NULL) { nd = *t; *t = '\0'; } } else md = '\0'; if (lookup_host(av, (struct in_addr *)&d[0]) != 0) errx(EX_NOHOST, "hostname ``%s'' unknown", av); switch (md) { case ':': if (!inet_aton(p, (struct in_addr *)&d[1])) errx(EX_DATAERR, "bad netmask ``%s''", p); break; case '/': masklen = atoi(p); if (masklen == 0) d[1] = htonl(0U); /* mask */ else if (masklen > 32) errx(EX_DATAERR, "bad width ``%s''", p); else d[1] = htonl(~0U << (32 - masklen)); break; case '{': /* no mask, assume /24 and put back the '{' */ d[1] = htonl(~0U << (32 - 24)); *(--p) = md; break; case ',': /* single address plus continuation */ *(--p) = md; /* FALLTHROUGH */ case 0: /* initialization value */ default: d[1] = htonl(~0U); /* force /32 */ break; } d[0] &= d[1]; /* mask base address with mask */ if (t) *t = nd; /* find next separator */ if (p) p = strpbrk(p, ",{"); if (p && *p == '{') { /* * We have a set of addresses. They are stored as follows: * arg1 is the set size (powers of 2, 2..256) * addr is the base address IN HOST FORMAT * mask.. is an array of arg1 bits (rounded up to * the next multiple of 32) with bits set * for each host in the map. */ uint32_t *map = (uint32_t *)&cmd->mask; int low, high; int i = contigmask((uint8_t *)&(d[1]), 32); if (len > 0) errx(EX_DATAERR, "address set cannot be in a list"); if (i < 24 || i > 31) errx(EX_DATAERR, "invalid set with mask %d\n", i); cmd->o.arg1 = 1<<(32-i); /* map length */ d[0] = ntohl(d[0]); /* base addr in host format */ cmd->o.opcode = O_IP_DST_SET; /* default */ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32; for (i = 0; i < (cmd->o.arg1+31)/32 ; i++) map[i] = 0; /* clear map */ av = p + 1; low = d[0] & 0xff; high = low + cmd->o.arg1 - 1; /* * Here, i stores the previous value when we specify a range * of addresses within a mask, e.g. 45-63. i = -1 means we * have no previous value. */ i = -1; /* previous value in a range */ while (isdigit(*av)) { char *s; int a = strtol(av, &s, 0); if (s == av) { /* no parameter */ if (*av != '}') errx(EX_DATAERR, "set not closed\n"); if (i != -1) errx(EX_DATAERR, "incomplete range %d-", i); break; } if (a < low || a > high) errx(EX_DATAERR, "addr %d out of range [%d-%d]\n", a, low, high); a -= low; if (i == -1) /* no previous in range */ i = a; else { /* check that range is valid */ if (i > a) errx(EX_DATAERR, "invalid range %d-%d", i+low, a+low); if (*s == '-') errx(EX_DATAERR, "double '-' in range"); } for (; i <= a; i++) map[i/32] |= 1<<(i & 31); i = -1; if (*s == '-') i = a; else if (*s == '}') break; av = s+1; } return; } av = p; if (av) /* then *av must be a ',' */ av++; /* Check this entry */ if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */ /* * 'any' turns the entire list into a NOP. * 'not any' never matches, so it is removed from the * list unless it is the only item, in which case we * report an error. */ if (cmd->o.len & F_NOT) { /* "not any" never matches */ if (av == NULL && len == 0) /* only this entry */ errx(EX_DATAERR, "not any never matches"); } /* else do nothing and skip this entry */ return; } /* A single IP can be stored in an optimized format */ if (d[1] == (uint32_t)~0 && av == NULL && len == 0) { cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); return; } len += 2; /* two words... */ d += 2; } /* end while */ if (len + 1 > F_LEN_MASK) errx(EX_DATAERR, "address list too long"); cmd->o.len |= len+1; } /* n2mask sets n bits of the mask */ void n2mask(struct in6_addr *mask, int n) { static int minimask[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; u_char *p; memset(mask, 0, sizeof(struct in6_addr)); p = (u_char *) mask; for (; n > 0; p++, n -= 8) { if (n >= 8) *p = 0xff; else *p = minimask[n]; } return; } static void fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, struct _s_x *flags, char *p) { char *e; uint32_t set = 0, clear = 0; if (fill_flags(flags, p, &e, &set, &clear) != 0) errx(EX_DATAERR, "invalid flag %s", e); cmd->opcode = opcode; cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); } void ipfw_delete(char *av[]) { ipfw_range_tlv rt; char *sep; int i, j; int exitval = EX_OK; int do_set = 0; av++; NEED1("missing rule specification"); if ( *av && _substrcmp(*av, "set") == 0) { /* Do not allow using the following syntax: * ipfw set N delete set M */ if (co.use_set) errx(EX_DATAERR, "invalid syntax"); do_set = 1; /* delete set */ av++; } /* Rule number */ while (*av && isdigit(**av)) { i = strtol(*av, &sep, 10); j = i; if (*sep== '-') j = strtol(sep + 1, NULL, 10); av++; if (co.do_nat) { exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i); if (exitval) { exitval = EX_UNAVAILABLE; if (co.do_quiet) continue; warn("nat %u not available", i); } } else if (co.do_pipe) { exitval = ipfw_delete_pipe(co.do_pipe, i); } else { memset(&rt, 0, sizeof(rt)); if (do_set != 0) { rt.set = i & 31; rt.flags = IPFW_RCFLAG_SET; } else { rt.start_rule = i & 0xffff; rt.end_rule = j & 0xffff; if (rt.start_rule == 0 && rt.end_rule == 0) rt.flags |= IPFW_RCFLAG_ALL; else rt.flags |= IPFW_RCFLAG_RANGE; if (co.use_set != 0) { rt.set = co.use_set - 1; rt.flags |= IPFW_RCFLAG_SET; } } if (co.do_dynamic == 2) rt.flags |= IPFW_RCFLAG_DYNAMIC; i = do_range_cmd(IP_FW_XDEL, &rt); if (i != 0) { exitval = EX_UNAVAILABLE; if (co.do_quiet) continue; warn("rule %u: setsockopt(IP_FW_XDEL)", rt.start_rule); } else if (rt.new_set == 0 && do_set == 0 && co.do_dynamic != 2) { exitval = EX_UNAVAILABLE; if (co.do_quiet) continue; if (rt.start_rule != rt.end_rule) warnx("no rules rules in %u-%u range", rt.start_rule, rt.end_rule); else warnx("rule %u not found", rt.start_rule); } } } if (exitval != EX_OK && co.do_force == 0) exit(exitval); } /* * fill the interface structure. We do not check the name as we can * create interfaces dynamically, so checking them at insert time * makes relatively little sense. * Interface names containing '*', '?', or '[' are assumed to be shell * patterns which match interfaces. */ static void fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate) { char *p; uint16_t uidx; cmd->name[0] = '\0'; cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); CHECK_CMDLEN; /* Parse the interface or address */ if (strcmp(arg, "any") == 0) cmd->o.len = 0; /* effectively ignore this command */ else if (strncmp(arg, "table(", 6) == 0) { if ((p = strchr(arg + 6, ')')) == NULL) errx(EX_DATAERR, "forgotten parenthesis: '%s'", arg); *p = '\0'; p = strchr(arg + 6, ','); if (p) *p++ = '\0'; if ((uidx = pack_table(tstate, arg + 6)) == 0) errx(EX_DATAERR, "Invalid table name: %s", arg + 6); cmd->name[0] = '\1'; /* Special value indicating table */ cmd->p.kidx = uidx; } else if (!isdigit(*arg)) { strlcpy(cmd->name, arg, sizeof(cmd->name)); cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0; } else if (!inet_aton(arg, &cmd->p.ip)) errx(EX_DATAERR, "bad ip address ``%s''", arg); } static void get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask) { int i; size_t l; char *ap, *ptr, *optr; struct ether_addr *mac; const char *macset = "0123456789abcdefABCDEF:"; if (strcmp(p, "any") == 0) { for (i = 0; i < ETHER_ADDR_LEN; i++) addr[i] = mask[i] = 0; return; } optr = ptr = strdup(p); if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) { l = strlen(ap); if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL) errx(EX_DATAERR, "Incorrect MAC address"); bcopy(mac, addr, ETHER_ADDR_LEN); } else errx(EX_DATAERR, "Incorrect MAC address"); if (ptr != NULL) { /* we have mask? */ if (p[ptr - optr - 1] == '/') { /* mask len */ long ml = strtol(ptr, &ap, 10); if (*ap != 0 || ml > ETHER_ADDR_LEN * 8 || ml < 0) errx(EX_DATAERR, "Incorrect mask length"); for (i = 0; ml > 0 && i < ETHER_ADDR_LEN; ml -= 8, i++) mask[i] = (ml >= 8) ? 0xff: (~0) << (8 - ml); } else { /* mask */ l = strlen(ptr); if (strspn(ptr, macset) != l || (mac = ether_aton(ptr)) == NULL) errx(EX_DATAERR, "Incorrect mask"); bcopy(mac, mask, ETHER_ADDR_LEN); } } else { /* default mask: ff:ff:ff:ff:ff:ff */ for (i = 0; i < ETHER_ADDR_LEN; i++) mask[i] = 0xff; } for (i = 0; i < ETHER_ADDR_LEN; i++) addr[i] &= mask[i]; free(optr); } /* * helper function, updates the pointer to cmd with the length * of the current command, and also cleans up the first word of * the new command in case it has been clobbered before. */ static ipfw_insn * next_cmd(ipfw_insn *cmd, int *len) { *len -= F_LEN(cmd); CHECK_LENGTH(*len, 0); cmd += F_LEN(cmd); bzero(cmd, sizeof(*cmd)); return cmd; } /* * Takes arguments and copies them into a comment */ static void fill_comment(ipfw_insn *cmd, char **av, int cblen) { int i, l; char *p = (char *)(cmd + 1); cmd->opcode = O_NOP; cmd->len = (cmd->len & (F_NOT | F_OR)); /* Compute length of comment string. */ for (i = 0, l = 0; av[i] != NULL; i++) l += strlen(av[i]) + 1; if (l == 0) return; if (l > 84) errx(EX_DATAERR, "comment too long (max 80 chars)"); l = 1 + (l+3)/4; cmd->len = (cmd->len & (F_NOT | F_OR)) | l; CHECK_CMDLEN; for (i = 0; av[i] != NULL; i++) { strcpy(p, av[i]); p += strlen(av[i]); *p++ = ' '; } *(--p) = '\0'; } /* * A function to fill simple commands of size 1. * Existing flags are preserved. */ static void fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg) { cmd->opcode = opcode; cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1; cmd->arg1 = arg; } /* * Fetch and add the MAC address and type, with masks. This generates one or * two microinstructions, and returns the pointer to the last one. */ static ipfw_insn * add_mac(ipfw_insn *cmd, char *av[], int cblen) { ipfw_insn_mac *mac; if ( ( av[0] == NULL ) || ( av[1] == NULL ) ) errx(EX_DATAERR, "MAC dst src"); cmd->opcode = O_MACADDR2; cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); CHECK_CMDLEN; mac = (ipfw_insn_mac *)cmd; get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]), &(mac->mask[ETHER_ADDR_LEN])); /* src */ return cmd; } static ipfw_insn * add_mactype(ipfw_insn *cmd, char *av, int cblen) { if (!av) errx(EX_DATAERR, "missing MAC type"); if (strcmp(av, "any") != 0) { /* we have a non-null type */ fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE, cblen); cmd->opcode = O_MAC_TYPE; return cmd; } else return NULL; } static ipfw_insn * add_proto0(ipfw_insn *cmd, char *av, u_char *protop) { struct protoent *pe; char *ep; int proto; proto = strtol(av, &ep, 10); if (*ep != '\0' || proto <= 0) { if ((pe = getprotobyname(av)) == NULL) return NULL; proto = pe->p_proto; } fill_cmd(cmd, O_PROTO, 0, proto); *protop = proto; return cmd; } static ipfw_insn * add_proto(ipfw_insn *cmd, char *av, u_char *protop) { u_char proto = IPPROTO_IP; if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) ; /* do not set O_IP4 nor O_IP6 */ else if (strcmp(av, "ip4") == 0) /* explicit "just IPv4" rule */ fill_cmd(cmd, O_IP4, 0, 0); else if (strcmp(av, "ip6") == 0) { /* explicit "just IPv6" rule */ proto = IPPROTO_IPV6; fill_cmd(cmd, O_IP6, 0, 0); } else return add_proto0(cmd, av, protop); *protop = proto; return cmd; } static ipfw_insn * add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop) { u_char proto = IPPROTO_IP; if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) ; /* do not set O_IP4 nor O_IP6 */ else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0) /* explicit "just IPv4" rule */ fill_cmd(cmd, O_IP4, 0, 0); else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) { /* explicit "just IPv6" rule */ proto = IPPROTO_IPV6; fill_cmd(cmd, O_IP6, 0, 0); } else return add_proto0(cmd, av, protop); *protop = proto; return cmd; } static ipfw_insn * add_srcip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) { fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate); if (cmd->opcode == O_IP_DST_SET) /* set */ cmd->opcode = O_IP_SRC_SET; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ cmd->opcode = O_IP_SRC_LOOKUP; else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ cmd->opcode = O_IP_SRC_ME; else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ cmd->opcode = O_IP_SRC; else /* addr/mask */ cmd->opcode = O_IP_SRC_MASK; return cmd; } static ipfw_insn * add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) { fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate); if (cmd->opcode == O_IP_DST_SET) /* set */ ; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ ; else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ cmd->opcode = O_IP_DST_ME; else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ cmd->opcode = O_IP_DST; else /* addr/mask */ cmd->opcode = O_IP_DST_MASK; return cmd; } static struct _s_x f_reserved_keywords[] = { { "altq", TOK_OR }, { "//", TOK_OR }, { "diverted", TOK_OR }, { "dst-port", TOK_OR }, { "src-port", TOK_OR }, { "established", TOK_OR }, { "keep-state", TOK_OR }, { "frag", TOK_OR }, { "icmptypes", TOK_OR }, { "in", TOK_OR }, { "out", TOK_OR }, { "ip6", TOK_OR }, { "any", TOK_OR }, { "to", TOK_OR }, { "via", TOK_OR }, { "{", TOK_OR }, { NULL, 0 } /* terminator */ }; static ipfw_insn * add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen) { if (match_token(f_reserved_keywords, av) != -1) return (NULL); if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) { /* XXX todo: check that we have a protocol with ports */ cmd->opcode = opcode; return cmd; } return NULL; } static ipfw_insn * add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) { struct in6_addr a; char *host, *ch, buf[INET6_ADDRSTRLEN]; ipfw_insn *ret = NULL; int len; /* Copy first address in set if needed */ if ((ch = strpbrk(av, "/,")) != NULL) { len = ch - av; strlcpy(buf, av, sizeof(buf)); if (len < sizeof(buf)) buf[len] = '\0'; host = buf; } else host = av; if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || inet_pton(AF_INET6, host, &a) == 1) ret = add_srcip6(cmd, av, cblen, tstate); /* XXX: should check for IPv4, not !IPv6 */ if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || inet_pton(AF_INET6, host, &a) != 1)) ret = add_srcip(cmd, av, cblen, tstate); if (ret == NULL && strcmp(av, "any") != 0) ret = cmd; return ret; } static ipfw_insn * add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) { struct in6_addr a; char *host, *ch, buf[INET6_ADDRSTRLEN]; ipfw_insn *ret = NULL; int len; /* Copy first address in set if needed */ if ((ch = strpbrk(av, "/,")) != NULL) { len = ch - av; strlcpy(buf, av, sizeof(buf)); if (len < sizeof(buf)) buf[len] = '\0'; host = buf; } else host = av; if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || inet_pton(AF_INET6, host, &a) == 1) ret = add_dstip6(cmd, av, cblen, tstate); /* XXX: should check for IPv4, not !IPv6 */ if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || inet_pton(AF_INET6, host, &a) != 1)) ret = add_dstip(cmd, av, cblen, tstate); if (ret == NULL && strcmp(av, "any") != 0) ret = cmd; return ret; } /* * Parse arguments and assemble the microinstructions which make up a rule. * Rules are added into the 'rulebuf' and then copied in the correct order * into the actual rule. * * The syntax for a rule starts with the action, followed by * optional action parameters, and the various match patterns. * In the assembled microcode, the first opcode must be an O_PROBE_STATE * (generated if the rule includes a keep-state option), then the * various match patterns, log/altq actions, and the actual action. * */ void compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) { /* * rules are added into the 'rulebuf' and then copied in * the correct order into the actual rule. * Some things that need to go out of order (prob, action etc.) * go into actbuf[]. */ static uint32_t actbuf[255], cmdbuf[255]; int rblen, ablen, cblen; ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; ipfw_insn *first_cmd; /* first match pattern */ struct ip_fw_rule *rule; /* * various flags used to record that we entered some fields. */ ipfw_insn *have_state = NULL; /* any state-related option */ int have_rstate = 0; ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL; ipfw_insn *have_skipcmd = NULL; size_t len; int i; int open_par = 0; /* open parenthesis ( */ /* proto is here because it is used to fetch ports */ u_char proto = IPPROTO_IP; /* default protocol */ double match_prob = 1; /* match probability, default is always match */ bzero(actbuf, sizeof(actbuf)); /* actions go here */ bzero(cmdbuf, sizeof(cmdbuf)); bzero(rbuf, *rbufsize); rule = (struct ip_fw_rule *)rbuf; cmd = (ipfw_insn *)cmdbuf; action = (ipfw_insn *)actbuf; rblen = *rbufsize / sizeof(uint32_t); rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t); ablen = sizeof(actbuf) / sizeof(actbuf[0]); cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]); cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1; #define CHECK_RBUFLEN(len) { CHECK_LENGTH(rblen, len); rblen -= len; } #define CHECK_ACTLEN CHECK_LENGTH(ablen, action->len) av++; /* [rule N] -- Rule number optional */ if (av[0] && isdigit(**av)) { rule->rulenum = atoi(*av); av++; } /* [set N] -- set number (0..RESVD_SET), optional */ if (av[0] && av[1] && _substrcmp(*av, "set") == 0) { int set = strtoul(av[1], NULL, 10); if (set < 0 || set > RESVD_SET) errx(EX_DATAERR, "illegal set %s", av[1]); rule->set = set; tstate->set = set; av += 2; } /* [prob D] -- match probability, optional */ if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) { match_prob = strtod(av[1], NULL); if (match_prob <= 0 || match_prob > 1) errx(EX_DATAERR, "illegal match prob. %s", av[1]); av += 2; } /* action -- mandatory */ NEED1("missing action"); i = match_token(rule_actions, *av); av++; action->len = 1; /* default */ CHECK_ACTLEN; switch(i) { case TOK_CHECKSTATE: have_state = action; action->opcode = O_CHECK_STATE; if (*av == NULL || match_token(rule_options, *av) == TOK_COMMENT) { action->arg1 = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); break; } if (*av[0] == ':') { if (strcmp(*av + 1, "any") == 0) action->arg1 = 0; else if (state_check_name(*av + 1) == 0) action->arg1 = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); else errx(EX_DATAERR, "Invalid state name %s", *av); av++; break; } errx(EX_DATAERR, "Invalid state name %s", *av); break; case TOK_ABORT: action->opcode = O_REJECT; action->arg1 = ICMP_REJECT_ABORT; break; case TOK_ABORT6: action->opcode = O_UNREACH6; action->arg1 = ICMP6_UNREACH_ABORT; break; case TOK_ACCEPT: action->opcode = O_ACCEPT; break; case TOK_DENY: action->opcode = O_DENY; action->arg1 = 0; break; case TOK_REJECT: action->opcode = O_REJECT; action->arg1 = ICMP_UNREACH_HOST; break; case TOK_RESET: action->opcode = O_REJECT; action->arg1 = ICMP_REJECT_RST; break; case TOK_RESET6: action->opcode = O_UNREACH6; action->arg1 = ICMP6_UNREACH_RST; break; case TOK_UNREACH: action->opcode = O_REJECT; NEED1("missing reject code"); fill_reject_code(&action->arg1, *av); av++; break; case TOK_UNREACH6: action->opcode = O_UNREACH6; NEED1("missing unreach code"); fill_unreach6_code(&action->arg1, *av); av++; break; case TOK_COUNT: action->opcode = O_COUNT; break; case TOK_NAT: action->opcode = O_NAT; action->len = F_INSN_SIZE(ipfw_insn_nat); CHECK_ACTLEN; if (*av != NULL && _substrcmp(*av, "global") == 0) { action->arg1 = IP_FW_NAT44_GLOBAL; av++; break; } else goto chkarg; case TOK_QUEUE: action->opcode = O_QUEUE; goto chkarg; case TOK_PIPE: action->opcode = O_PIPE; goto chkarg; case TOK_SKIPTO: action->opcode = O_SKIPTO; goto chkarg; case TOK_NETGRAPH: action->opcode = O_NETGRAPH; goto chkarg; case TOK_NGTEE: action->opcode = O_NGTEE; goto chkarg; case TOK_DIVERT: action->opcode = O_DIVERT; goto chkarg; case TOK_TEE: action->opcode = O_TEE; goto chkarg; case TOK_CALL: action->opcode = O_CALLRETURN; chkarg: if (!av[0]) errx(EX_USAGE, "missing argument for %s", *(av - 1)); if (isdigit(**av)) { action->arg1 = strtoul(*av, NULL, 10); if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG) errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); } else if (_substrcmp(*av, "tablearg") == 0) { action->arg1 = IP_FW_TARG; } else if (i == TOK_DIVERT || i == TOK_TEE) { struct servent *s; setservent(1); s = getservbyname(av[0], "divert"); if (s != NULL) action->arg1 = ntohs(s->s_port); else errx(EX_DATAERR, "illegal divert/tee port"); } else errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); av++; break; case TOK_FORWARD: { /* * Locate the address-port separator (':' or ','). * Could be one of the following: * hostname:port * IPv4 a.b.c.d,port * IPv4 a.b.c.d:port * IPv6 w:x:y::z,port * The ':' can only be used with hostname and IPv4 address. * XXX-BZ Should we also support [w:x:y::z]:port? */ struct sockaddr_storage result; struct addrinfo *res; char *s, *end; int family; u_short port_number; NEED1("missing forward address[:port]"); /* * locate the address-port separator (':' or ',') */ s = strchr(*av, ','); if (s == NULL) { /* Distinguish between IPv4:port and IPv6 cases. */ s = strchr(*av, ':'); if (s && strchr(s+1, ':')) s = NULL; /* no port */ } port_number = 0; if (s != NULL) { /* Terminate host portion and set s to start of port. */ *(s++) = '\0'; i = strtoport(s, &end, 0 /* base */, 0 /* proto */); if (s == end) errx(EX_DATAERR, "illegal forwarding port ``%s''", s); port_number = (u_short)i; } if (_substrcmp(*av, "tablearg") == 0) { family = PF_INET; ((struct sockaddr_in*)&result)->sin_addr.s_addr = INADDR_ANY; } else { /* * Resolve the host name or address to a family and a * network representation of the address. */ if (getaddrinfo(*av, NULL, NULL, &res)) errx(EX_DATAERR, NULL); /* Just use the first host in the answer. */ family = res->ai_family; memcpy(&result, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); } if (family == PF_INET) { ipfw_insn_sa *p = (ipfw_insn_sa *)action; action->opcode = O_FORWARD_IP; action->len = F_INSN_SIZE(ipfw_insn_sa); CHECK_ACTLEN; /* * In the kernel we assume AF_INET and use only * sin_port and sin_addr. Remember to set sin_len as * the routing code seems to use it too. */ p->sa.sin_len = sizeof(struct sockaddr_in); p->sa.sin_family = AF_INET; p->sa.sin_port = port_number; p->sa.sin_addr.s_addr = ((struct sockaddr_in *)&result)->sin_addr.s_addr; } else if (family == PF_INET6) { ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action; action->opcode = O_FORWARD_IP6; action->len = F_INSN_SIZE(ipfw_insn_sa6); CHECK_ACTLEN; p->sa.sin6_len = sizeof(struct sockaddr_in6); p->sa.sin6_family = AF_INET6; p->sa.sin6_port = port_number; p->sa.sin6_flowinfo = 0; p->sa.sin6_scope_id = ((struct sockaddr_in6 *)&result)->sin6_scope_id; bcopy(&((struct sockaddr_in6*)&result)->sin6_addr, &p->sa.sin6_addr, sizeof(p->sa.sin6_addr)); } else { errx(EX_DATAERR, "Invalid address family in forward action"); } av++; break; } case TOK_COMMENT: /* pretend it is a 'count' rule followed by the comment */ action->opcode = O_COUNT; av--; /* go back... */ break; case TOK_SETFIB: { int numfibs; size_t intsize = sizeof(int); action->opcode = O_SETFIB; NEED1("missing fib number"); if (_substrcmp(*av, "tablearg") == 0) { action->arg1 = IP_FW_TARG; } else { action->arg1 = strtoul(*av, NULL, 10); if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) errx(EX_DATAERR, "fibs not suported.\n"); if (action->arg1 >= numfibs) /* Temporary */ errx(EX_DATAERR, "fib too large.\n"); /* Add high-order bit to fib to make room for tablearg*/ action->arg1 |= 0x8000; } av++; break; } case TOK_SETDSCP: { int code; action->opcode = O_SETDSCP; NEED1("missing DSCP code"); if (_substrcmp(*av, "tablearg") == 0) { action->arg1 = IP_FW_TARG; } else { if (isalpha(*av[0])) { if ((code = match_token(f_ipdscp, *av)) == -1) errx(EX_DATAERR, "Unknown DSCP code"); action->arg1 = code; } else action->arg1 = strtoul(*av, NULL, 10); /* * Add high-order bit to DSCP to make room * for tablearg */ action->arg1 |= 0x8000; } av++; break; } case TOK_REASS: action->opcode = O_REASS; break; case TOK_RETURN: fill_cmd(action, O_CALLRETURN, F_NOT, 0); break; case TOK_TCPSETMSS: { u_long mss; uint16_t idx; idx = pack_object(tstate, "tcp-setmss", IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); NEED1("Missing MSS value"); action = next_cmd(action, &ablen); action->len = 1; CHECK_ACTLEN; mss = strtoul(*av, NULL, 10); if (mss == 0 || mss > UINT16_MAX) errx(EX_USAGE, "invalid MSS value %s", *av); fill_cmd(action, O_EXTERNAL_DATA, 0, (uint16_t)mss); av++; break; } default: av--; if (match_token(rule_eactions, *av) == -1) errx(EX_DATAERR, "invalid action %s\n", *av); /* * External actions support. * XXX: we support only syntax with instance name. * For known external actions (from rule_eactions list) * we can handle syntax directly. But with `eaction' * keyword we can use only `eaction ' * syntax. */ case TOK_EACTION: { uint16_t idx; NEED1("Missing eaction name"); if (eaction_check_name(*av) != 0) errx(EX_DATAERR, "Invalid eaction name %s", *av); idx = pack_object(tstate, *av, IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); av++; NEED1("Missing eaction instance name"); action = next_cmd(action, &ablen); action->len = 1; CHECK_ACTLEN; if (eaction_check_name(*av) != 0) errx(EX_DATAERR, "Invalid eaction instance name %s", *av); /* * External action instance object has TLV type depended * from the external action name object index. Since we * currently don't know this index, use zero as TLV type. */ idx = pack_object(tstate, *av, 0); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); fill_cmd(action, O_EXTERNAL_INSTANCE, 0, idx); av++; } } action = next_cmd(action, &ablen); /* * [altq queuename] -- altq tag, optional * [log [logamount N]] -- log, optional * * If they exist, it go first in the cmdbuf, but then it is * skipped in the copy section to the end of the buffer. */ while (av[0] != NULL && (i = match_token(rule_action_params, *av)) != -1) { av++; switch (i) { case TOK_LOG: { ipfw_insn_log *c = (ipfw_insn_log *)cmd; int l; if (have_log) errx(EX_DATAERR, "log cannot be specified more than once"); have_log = (ipfw_insn *)c; cmd->len = F_INSN_SIZE(ipfw_insn_log); CHECK_CMDLEN; cmd->opcode = O_LOG; if (av[0] && _substrcmp(*av, "logamount") == 0) { av++; NEED1("logamount requires argument"); l = atoi(*av); if (l < 0) errx(EX_DATAERR, "logamount must be positive"); c->max_log = l; av++; } else { len = sizeof(c->max_log); if (sysctlbyname("net.inet.ip.fw.verbose_limit", &c->max_log, &len, NULL, 0) == -1) { if (co.test_only) { c->max_log = 0; break; } errx(1, "sysctlbyname(\"%s\")", "net.inet.ip.fw.verbose_limit"); } } } break; #ifndef NO_ALTQ case TOK_ALTQ: { ipfw_insn_altq *a = (ipfw_insn_altq *)cmd; NEED1("missing altq queue name"); if (have_altq) errx(EX_DATAERR, "altq cannot be specified more than once"); have_altq = (ipfw_insn *)a; cmd->len = F_INSN_SIZE(ipfw_insn_altq); CHECK_CMDLEN; cmd->opcode = O_ALTQ; a->qid = altq_name_to_qid(*av); av++; } break; #endif case TOK_TAG: case TOK_UNTAG: { uint16_t tag; if (have_tag) errx(EX_USAGE, "tag and untag cannot be " "specified more than once"); GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, i, rule_action_params); have_tag = cmd; fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag); av++; break; } default: abort(); } cmd = next_cmd(cmd, &cblen); } if (have_state) { /* must be a check-state, we are done */ if (*av != NULL && match_token(rule_options, *av) == TOK_COMMENT) { /* check-state has a comment */ av++; fill_comment(cmd, av, cblen); cmd = next_cmd(cmd, &cblen); av[0] = NULL; } goto done; } #define OR_START(target) \ if (av[0] && (*av[0] == '(' || *av[0] == '{')) { \ if (open_par) \ errx(EX_USAGE, "nested \"(\" not allowed\n"); \ prev = NULL; \ open_par = 1; \ if ( (av[0])[1] == '\0') { \ av++; \ } else \ (*av)++; \ } \ target: \ #define CLOSE_PAR \ if (open_par) { \ if (av[0] && ( \ strcmp(*av, ")") == 0 || \ strcmp(*av, "}") == 0)) { \ prev = NULL; \ open_par = 0; \ av++; \ } else \ errx(EX_USAGE, "missing \")\"\n"); \ } #define NOT_BLOCK \ if (av[0] && _substrcmp(*av, "not") == 0) { \ if (cmd->len & F_NOT) \ errx(EX_USAGE, "double \"not\" not allowed\n"); \ cmd->len |= F_NOT; \ av++; \ } #define OR_BLOCK(target) \ if (av[0] && _substrcmp(*av, "or") == 0) { \ if (prev == NULL || open_par == 0) \ errx(EX_DATAERR, "invalid OR block"); \ prev->len |= F_OR; \ av++; \ goto target; \ } \ CLOSE_PAR; first_cmd = cmd; #if 0 /* * MAC addresses, optional. * If we have this, we skip the part "proto from src to dst" * and jump straight to the option parsing. */ NOT_BLOCK; NEED1("missing protocol"); if (_substrcmp(*av, "MAC") == 0 || _substrcmp(*av, "mac") == 0) { av++; /* the "MAC" keyword */ add_mac(cmd, av); /* exits in case of errors */ cmd = next_cmd(cmd); av += 2; /* dst-mac and src-mac */ NOT_BLOCK; NEED1("missing mac type"); if (add_mactype(cmd, av[0])) cmd = next_cmd(cmd); av++; /* any or mac-type */ goto read_options; } #endif /* * protocol, mandatory */ OR_START(get_proto); NOT_BLOCK; NEED1("missing protocol"); if (add_proto_compat(cmd, *av, &proto)) { av++; if (F_LEN(cmd) != 0) { prev = cmd; cmd = next_cmd(cmd, &cblen); } } else if (first_cmd != cmd) { errx(EX_DATAERR, "invalid protocol ``%s''", *av); } else { rule->flags |= IPFW_RULE_JUSTOPTS; goto read_options; } OR_BLOCK(get_proto); /* * "from", mandatory */ if ((av[0] == NULL) || _substrcmp(*av, "from") != 0) errx(EX_USAGE, "missing ``from''"); av++; /* * source IP, mandatory */ OR_START(source_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing source address"); if (add_src(cmd, *av, proto, cblen, tstate)) { av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; cmd = next_cmd(cmd, &cblen); } } else errx(EX_USAGE, "bad source address %s", *av); OR_BLOCK(source_ip); /* * source ports, optional */ NOT_BLOCK; /* optional "not" */ if ( av[0] != NULL ) { if (_substrcmp(*av, "any") == 0 || add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) { av++; if (F_LEN(cmd) != 0) cmd = next_cmd(cmd, &cblen); } } /* * "to", mandatory */ if ( (av[0] == NULL) || _substrcmp(*av, "to") != 0 ) errx(EX_USAGE, "missing ``to''"); av++; /* * destination, mandatory */ OR_START(dest_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing dst address"); if (add_dst(cmd, *av, proto, cblen, tstate)) { av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; cmd = next_cmd(cmd, &cblen); } } else errx( EX_USAGE, "bad destination address %s", *av); OR_BLOCK(dest_ip); /* * dest. ports, optional */ NOT_BLOCK; /* optional "not" */ if (av[0]) { if (_substrcmp(*av, "any") == 0 || add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) { av++; if (F_LEN(cmd) != 0) cmd = next_cmd(cmd, &cblen); } } read_options: prev = NULL; while ( av[0] != NULL ) { char *s; ipfw_insn_u32 *cmd32; /* alias for cmd */ s = *av; cmd32 = (ipfw_insn_u32 *)cmd; if (*s == '!') { /* alternate syntax for NOT */ if (cmd->len & F_NOT) errx(EX_USAGE, "double \"not\" not allowed\n"); cmd->len = F_NOT; s++; } i = match_token(rule_options, s); av++; switch(i) { case TOK_NOT: if (cmd->len & F_NOT) errx(EX_USAGE, "double \"not\" not allowed\n"); cmd->len = F_NOT; break; case TOK_OR: if (open_par == 0 || prev == NULL) errx(EX_USAGE, "invalid \"or\" block\n"); prev->len |= F_OR; break; case TOK_STARTBRACE: if (open_par) errx(EX_USAGE, "+nested \"(\" not allowed\n"); open_par = 1; break; case TOK_ENDBRACE: if (!open_par) errx(EX_USAGE, "+missing \")\"\n"); open_par = 0; prev = NULL; break; case TOK_IN: fill_cmd(cmd, O_IN, 0, 0); break; case TOK_OUT: cmd->len ^= F_NOT; /* toggle F_NOT */ fill_cmd(cmd, O_IN, 0, 0); break; case TOK_DIVERTED: fill_cmd(cmd, O_DIVERTED, 0, 3); break; case TOK_DIVERTEDLOOPBACK: fill_cmd(cmd, O_DIVERTED, 0, 1); break; case TOK_DIVERTEDOUTPUT: fill_cmd(cmd, O_DIVERTED, 0, 2); break; case TOK_FRAG: fill_cmd(cmd, O_FRAG, 0, 0); break; case TOK_LAYER2: fill_cmd(cmd, O_LAYER2, 0, 0); break; case TOK_XMIT: case TOK_RECV: case TOK_VIA: NEED1("recv, xmit, via require interface name" " or address"); fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate); av++; if (F_LEN(cmd) == 0) /* not a valid address */ break; if (i == TOK_XMIT) cmd->opcode = O_XMIT; else if (i == TOK_RECV) cmd->opcode = O_RECV; else if (i == TOK_VIA) cmd->opcode = O_VIA; break; case TOK_ICMPTYPES: NEED1("icmptypes requires list of types"); fill_icmptypes((ipfw_insn_u32 *)cmd, *av); av++; break; case TOK_ICMP6TYPES: NEED1("icmptypes requires list of types"); fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av, cblen); av++; break; case TOK_IPTTL: NEED1("ipttl requires TTL"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_IPTTL, cblen)) errx(EX_DATAERR, "invalid ipttl %s", *av); } else fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_IPID: NEED1("ipid requires id"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_IPID, cblen)) errx(EX_DATAERR, "invalid ipid %s", *av); } else fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_IPLEN: NEED1("iplen requires length"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_IPLEN, cblen)) errx(EX_DATAERR, "invalid ip len %s", *av); } else fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_IPVER: NEED1("ipver requires version"); fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_IPPRECEDENCE: NEED1("ipprecedence requires value"); fill_cmd(cmd, O_IPPRECEDENCE, 0, (strtoul(*av, NULL, 0) & 7) << 5); av++; break; case TOK_DSCP: NEED1("missing DSCP code"); fill_dscp(cmd, *av, cblen); av++; break; case TOK_IPOPTS: NEED1("missing argument for ipoptions"); fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av); av++; break; case TOK_IPTOS: NEED1("missing argument for iptos"); fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av); av++; break; case TOK_UID: NEED1("uid requires argument"); { char *end; uid_t uid; struct passwd *pwd; cmd->opcode = O_UID; uid = strtoul(*av, &end, 0); pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av); if (pwd == NULL) errx(EX_DATAERR, "uid \"%s\" nonexistent", *av); cmd32->d[0] = pwd->pw_uid; cmd->len |= F_INSN_SIZE(ipfw_insn_u32); av++; } break; case TOK_GID: NEED1("gid requires argument"); { char *end; gid_t gid; struct group *grp; cmd->opcode = O_GID; gid = strtoul(*av, &end, 0); grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av); if (grp == NULL) errx(EX_DATAERR, "gid \"%s\" nonexistent", *av); cmd32->d[0] = grp->gr_gid; cmd->len |= F_INSN_SIZE(ipfw_insn_u32); av++; } break; case TOK_JAIL: NEED1("jail requires argument"); { int jid; cmd->opcode = O_JAIL; jid = jail_getid(*av); if (jid < 0) errx(EX_DATAERR, "%s", jail_errmsg); cmd32->d[0] = (uint32_t)jid; cmd->len |= F_INSN_SIZE(ipfw_insn_u32); av++; } break; case TOK_ESTAB: fill_cmd(cmd, O_ESTAB, 0, 0); break; case TOK_SETUP: fill_cmd(cmd, O_TCPFLAGS, 0, (TH_SYN) | ( (TH_ACK) & 0xff) <<8 ); break; case TOK_TCPDATALEN: NEED1("tcpdatalen requires length"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_TCPDATALEN, cblen)) errx(EX_DATAERR, "invalid tcpdata len %s", *av); } else fill_cmd(cmd, O_TCPDATALEN, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_TCPOPTS: NEED1("missing argument for tcpoptions"); fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av); av++; break; case TOK_TCPSEQ: case TOK_TCPACK: NEED1("tcpseq/tcpack requires argument"); cmd->len = F_INSN_SIZE(ipfw_insn_u32); cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK; cmd32->d[0] = htonl(strtoul(*av, NULL, 0)); av++; break; case TOK_TCPWIN: NEED1("tcpwin requires length"); if (strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_TCPWIN, cblen)) errx(EX_DATAERR, "invalid tcpwin len %s", *av); } else fill_cmd(cmd, O_TCPWIN, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_TCPFLAGS: NEED1("missing argument for tcpflags"); cmd->opcode = O_TCPFLAGS; fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av); av++; break; case TOK_KEEPSTATE: case TOK_RECORDSTATE: { uint16_t uidx; if (open_par) errx(EX_USAGE, "keep-state or record-state cannot be part " "of an or block"); if (have_state) errx(EX_USAGE, "only one of keep-state, record-state, " " limit and set-limit is allowed"); if (*av != NULL && *av[0] == ':') { if (state_check_name(*av + 1) != 0) errx(EX_DATAERR, "Invalid state name %s", *av); uidx = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); av++; } else uidx = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); have_state = cmd; have_rstate = i == TOK_RECORDSTATE; fill_cmd(cmd, O_KEEP_STATE, 0, uidx); break; } case TOK_LIMIT: case TOK_SETLIMIT: { ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; int val; if (open_par) errx(EX_USAGE, "limit or set-limit cannot be part of an or block"); if (have_state) errx(EX_USAGE, "only one of keep-state, record-state, " " limit and set-limit is allowed"); have_state = cmd; have_rstate = i == TOK_SETLIMIT; cmd->len = F_INSN_SIZE(ipfw_insn_limit); CHECK_CMDLEN; cmd->opcode = O_LIMIT; c->limit_mask = c->conn_limit = 0; while ( av[0] != NULL ) { if ((val = match_token(limit_masks, *av)) <= 0) break; c->limit_mask |= val; av++; } if (c->limit_mask == 0) errx(EX_USAGE, "limit: missing limit mask"); GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX, TOK_LIMIT, rule_options); av++; if (*av != NULL && *av[0] == ':') { if (state_check_name(*av + 1) != 0) errx(EX_DATAERR, "Invalid state name %s", *av); cmd->arg1 = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); av++; } else cmd->arg1 = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); break; } case TOK_PROTO: NEED1("missing protocol"); if (add_proto(cmd, *av, &proto)) { av++; } else errx(EX_DATAERR, "invalid protocol ``%s''", *av); break; case TOK_SRCIP: NEED1("missing source IP"); if (add_srcip(cmd, *av, cblen, tstate)) { av++; } break; case TOK_DSTIP: NEED1("missing destination IP"); if (add_dstip(cmd, *av, cblen, tstate)) { av++; } break; case TOK_SRCIP6: NEED1("missing source IP6"); if (add_srcip6(cmd, *av, cblen, tstate)) { av++; } break; case TOK_DSTIP6: NEED1("missing destination IP6"); if (add_dstip6(cmd, *av, cblen, tstate)) { av++; } break; case TOK_SRCPORT: NEED1("missing source port"); if (_substrcmp(*av, "any") == 0 || add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) { av++; } else errx(EX_DATAERR, "invalid source port %s", *av); break; case TOK_DSTPORT: NEED1("missing destination port"); if (_substrcmp(*av, "any") == 0 || add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) { av++; } else errx(EX_DATAERR, "invalid destination port %s", *av); break; case TOK_MAC: if (add_mac(cmd, av, cblen)) av += 2; break; case TOK_MACTYPE: NEED1("missing mac type"); if (!add_mactype(cmd, *av, cblen)) errx(EX_DATAERR, "invalid mac type %s", *av); av++; break; case TOK_VERREVPATH: fill_cmd(cmd, O_VERREVPATH, 0, 0); break; case TOK_VERSRCREACH: fill_cmd(cmd, O_VERSRCREACH, 0, 0); break; case TOK_ANTISPOOF: fill_cmd(cmd, O_ANTISPOOF, 0, 0); break; case TOK_IPSEC: fill_cmd(cmd, O_IPSEC, 0, 0); break; case TOK_IPV6: fill_cmd(cmd, O_IP6, 0, 0); break; case TOK_IPV4: fill_cmd(cmd, O_IP4, 0, 0); break; case TOK_EXT6HDR: fill_ext6hdr( cmd, *av ); av++; break; case TOK_FLOWID: if (proto != IPPROTO_IPV6 ) errx( EX_USAGE, "flow-id filter is active " "only for ipv6 protocol\n"); fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen); av++; break; case TOK_COMMENT: fill_comment(cmd, av, cblen); av[0]=NULL; break; case TOK_TAGGED: if (av[0] && strpbrk(*av, "-,")) { if (!add_ports(cmd, *av, 0, O_TAGGED, cblen)) errx(EX_DATAERR, "tagged: invalid tag" " list: %s", *av); } else { uint16_t tag; GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, TOK_TAGGED, rule_options); fill_cmd(cmd, O_TAGGED, 0, tag); } av++; break; case TOK_FIB: NEED1("fib requires fib number"); fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0)); av++; break; case TOK_SOCKARG: fill_cmd(cmd, O_SOCKARG, 0, 0); break; case TOK_LOOKUP: { ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd; int j; if (!av[0] || !av[1]) errx(EX_USAGE, "format: lookup argument tablenum"); cmd->opcode = O_IP_DST_LOOKUP; cmd->len |= F_INSN_SIZE(ipfw_insn) + 2; i = match_token(rule_options, *av); for (j = 0; lookup_key[j] >= 0 ; j++) { if (i == lookup_key[j]) break; } if (lookup_key[j] <= 0) errx(EX_USAGE, "format: cannot lookup on %s", *av); __PAST_END(c->d, 1) = j; // i converted to option av++; if ((j = pack_table(tstate, *av)) == 0) errx(EX_DATAERR, "Invalid table name: %s", *av); cmd->arg1 = j; av++; } break; case TOK_FLOW: NEED1("missing table name"); if (strncmp(*av, "table(", 6) != 0) errx(EX_DATAERR, "enclose table name into \"table()\""); fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate); av++; break; case TOK_SKIPACTION: if (have_skipcmd) errx(EX_USAGE, "only one defer-action " "is allowed"); have_skipcmd = cmd; fill_cmd(cmd, O_SKIP_ACTION, 0, 0); break; default: errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); } if (F_LEN(cmd) > 0) { /* prepare to advance */ prev = cmd; cmd = next_cmd(cmd, &cblen); } } done: if (!have_state && have_skipcmd) warnx("Rule contains \"defer-immediate-action\" " "and doesn't contain any state-related options."); /* * Now copy stuff into the rule. * If we have a keep-state option, the first instruction * must be a PROBE_STATE (which is generated here). * If we have a LOG option, it was stored as the first command, * and now must be moved to the top of the action part. */ dst = (ipfw_insn *)rule->cmd; /* * First thing to write into the command stream is the match probability. */ if (match_prob != 1) { /* 1 means always match */ dst->opcode = O_PROB; dst->len = 2; *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff); dst += dst->len; } /* * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode != O_CHECK_STATE && !have_rstate) { fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1); dst = next_cmd(dst, &rblen); } /* * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG, * O_SKIP_ACTION */ for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { i = F_LEN(src); CHECK_RBUFLEN(i); switch (src->opcode) { case O_LOG: case O_KEEP_STATE: case O_LIMIT: case O_ALTQ: case O_TAG: case O_SKIP_ACTION: break; default: bcopy(src, dst, i * sizeof(uint32_t)); dst += i; } } /* * put back the have_state command as last opcode */ if (have_state && have_state->opcode != O_CHECK_STATE) { i = F_LEN(have_state); CHECK_RBUFLEN(i); bcopy(have_state, dst, i * sizeof(uint32_t)); dst += i; } /* * put back the have_skipcmd command as very last opcode */ if (have_skipcmd) { i = F_LEN(have_skipcmd); CHECK_RBUFLEN(i); bcopy(have_skipcmd, dst, i * sizeof(uint32_t)); dst += i; } /* * start action section */ rule->act_ofs = dst - rule->cmd; /* put back O_LOG, O_ALTQ, O_TAG if necessary */ if (have_log) { i = F_LEN(have_log); CHECK_RBUFLEN(i); bcopy(have_log, dst, i * sizeof(uint32_t)); dst += i; } if (have_altq) { i = F_LEN(have_altq); CHECK_RBUFLEN(i); bcopy(have_altq, dst, i * sizeof(uint32_t)); dst += i; } if (have_tag) { i = F_LEN(have_tag); CHECK_RBUFLEN(i); bcopy(have_tag, dst, i * sizeof(uint32_t)); dst += i; } /* * copy all other actions */ for (src = (ipfw_insn *)actbuf; src != action; src += i) { i = F_LEN(src); CHECK_RBUFLEN(i); bcopy(src, dst, i * sizeof(uint32_t)); dst += i; } rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd); *rbufsize = (char *)dst - (char *)rule; } static int compare_ntlv(const void *_a, const void *_b) { ipfw_obj_ntlv *a, *b; a = (ipfw_obj_ntlv *)_a; b = (ipfw_obj_ntlv *)_b; if (a->set < b->set) return (-1); else if (a->set > b->set) return (1); if (a->idx < b->idx) return (-1); else if (a->idx > b->idx) return (1); if (a->head.type < b->head.type) return (-1); else if (a->head.type > b->head.type) return (1); return (0); } /* * Provide kernel with sorted list of referenced objects */ static void object_sort_ctlv(ipfw_obj_ctlv *ctlv) { qsort(ctlv + 1, ctlv->count, ctlv->objsize, compare_ntlv); } struct object_kt { uint16_t uidx; uint16_t type; }; static int compare_object_kntlv(const void *k, const void *v) { ipfw_obj_ntlv *ntlv; struct object_kt key; key = *((struct object_kt *)k); ntlv = (ipfw_obj_ntlv *)v; if (key.uidx < ntlv->idx) return (-1); else if (key.uidx > ntlv->idx) return (1); if (key.type < ntlv->head.type) return (-1); else if (key.type > ntlv->head.type) return (1); return (0); } /* * Finds object name in @ctlv by @idx and @type. * Uses the following facts: * 1) All TLVs are the same size * 2) Kernel implementation provides already sorted list. * * Returns table name or NULL. */ static char * object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type) { ipfw_obj_ntlv *ntlv; struct object_kt key; key.uidx = idx; key.type = type; ntlv = bsearch(&key, (ctlv + 1), ctlv->count, ctlv->objsize, compare_object_kntlv); if (ntlv != NULL) return (ntlv->name); return (NULL); } static char * table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx) { return (object_search_ctlv(ctlv, idx, IPFW_TLV_TBL_NAME)); } /* * Adds one or more rules to ipfw chain. * Data layout: * Request: * [ * ip_fw3_opheader * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1) * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] (*2) (*3) * ] * Reply: * [ * ip_fw3_opheader * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] * ] * * Rules in reply are modified to store their actual ruleset number. * * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending * according to their idx field and there has to be no duplicates. * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending. * (*3) Each ip_fw structure needs to be aligned to u64 boundary. */ void ipfw_add(char *av[]) { uint32_t rulebuf[1024]; int rbufsize, default_off, tlen, rlen; size_t sz; struct tidx ts; struct ip_fw_rule *rule; caddr_t tbuf; ip_fw3_opheader *op3; ipfw_obj_ctlv *ctlv, *tstate; rbufsize = sizeof(rulebuf); memset(rulebuf, 0, rbufsize); memset(&ts, 0, sizeof(ts)); /* Optimize case with no tables */ default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader); op3 = (ip_fw3_opheader *)rulebuf; ctlv = (ipfw_obj_ctlv *)(op3 + 1); rule = (struct ip_fw_rule *)(ctlv + 1); rbufsize -= default_off; compile_rule(av, (uint32_t *)rule, &rbufsize, &ts); /* Align rule size to u64 boundary */ rlen = roundup2(rbufsize, sizeof(uint64_t)); tbuf = NULL; sz = 0; tstate = NULL; if (ts.count != 0) { /* Some tables. We have to alloc more data */ tlen = ts.count * sizeof(ipfw_obj_ntlv); sz = default_off + sizeof(ipfw_obj_ctlv) + tlen + rlen; if ((tbuf = calloc(1, sz)) == NULL) err(EX_UNAVAILABLE, "malloc() failed for IP_FW_ADD"); op3 = (ip_fw3_opheader *)tbuf; /* Tables first */ ctlv = (ipfw_obj_ctlv *)(op3 + 1); ctlv->head.type = IPFW_TLV_TBLNAME_LIST; ctlv->head.length = sizeof(ipfw_obj_ctlv) + tlen; ctlv->count = ts.count; ctlv->objsize = sizeof(ipfw_obj_ntlv); memcpy(ctlv + 1, ts.idx, tlen); object_sort_ctlv(ctlv); tstate = ctlv; /* Rule next */ ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); ctlv->head.type = IPFW_TLV_RULE_LIST; ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen; ctlv->count = 1; memcpy(ctlv + 1, rule, rbufsize); } else { /* Simply add header */ sz = rlen + default_off; memset(ctlv, 0, sizeof(*ctlv)); ctlv->head.type = IPFW_TLV_RULE_LIST; ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen; ctlv->count = 1; } if (do_get3(IP_FW_XADD, op3, &sz) != 0) err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_XADD"); if (!co.do_quiet) { struct format_opts sfo; struct buf_pr bp; memset(&sfo, 0, sizeof(sfo)); sfo.tstate = tstate; sfo.set_mask = (uint32_t)(-1); bp_alloc(&bp, 4096); show_static_rule(&co, &sfo, &bp, rule, NULL); printf("%s", bp.buf); bp_free(&bp); } if (tbuf != NULL) free(tbuf); if (ts.idx != NULL) free(ts.idx); } /* * clear the counters or the log counters. * optname has the following values: * 0 (zero both counters and logging) * 1 (zero logging only) */ void ipfw_zero(int ac, char *av[], int optname) { ipfw_range_tlv rt; char const *errstr; char const *name = optname ? "RESETLOG" : "ZERO"; uint32_t arg; int failed = EX_OK; optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO; av++; ac--; if (ac == 0) { /* clear all entries */ memset(&rt, 0, sizeof(rt)); rt.flags = IPFW_RCFLAG_ALL; if (do_range_cmd(optname, &rt) < 0) err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name); if (!co.do_quiet) printf("%s.\n", optname == IP_FW_XZERO ? "Accounting cleared":"Logging counts reset"); return; } while (ac) { /* Rule number */ if (isdigit(**av)) { arg = strtonum(*av, 0, 0xffff, &errstr); if (errstr) errx(EX_DATAERR, "invalid rule number %s\n", *av); memset(&rt, 0, sizeof(rt)); rt.start_rule = arg; rt.end_rule = arg; rt.flags |= IPFW_RCFLAG_RANGE; if (co.use_set != 0) { rt.set = co.use_set - 1; rt.flags |= IPFW_RCFLAG_SET; } if (do_range_cmd(optname, &rt) != 0) { warn("rule %u: setsockopt(IP_FW_X%s)", arg, name); failed = EX_UNAVAILABLE; } else if (rt.new_set == 0) { printf("Entry %d not found\n", arg); failed = EX_UNAVAILABLE; } else if (!co.do_quiet) printf("Entry %d %s.\n", arg, optname == IP_FW_XZERO ? "cleared" : "logging count reset"); } else { errx(EX_USAGE, "invalid rule number ``%s''", *av); } av++; ac--; } if (failed != EX_OK) exit(failed); } void ipfw_flush(int force) { ipfw_range_tlv rt; if (!force && !co.do_quiet) { /* need to ask user */ int c; printf("Are you sure? [yn] "); fflush(stdout); do { c = toupper(getc(stdin)); while (c != '\n' && getc(stdin) != '\n') if (feof(stdin)) return; /* and do not flush */ } while (c != 'Y' && c != 'N'); printf("\n"); if (c == 'N') /* user said no */ return; } if (co.do_pipe) { dummynet_flush(); return; } /* `ipfw set N flush` - is the same that `ipfw delete set N` */ memset(&rt, 0, sizeof(rt)); if (co.use_set != 0) { rt.set = co.use_set - 1; rt.flags = IPFW_RCFLAG_SET; } else rt.flags = IPFW_RCFLAG_ALL; if (do_range_cmd(IP_FW_XDEL, &rt) != 0) err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)"); if (!co.do_quiet) printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules"); } static struct _s_x intcmds[] = { { "talist", TOK_TALIST }, { "iflist", TOK_IFLIST }, { "olist", TOK_OLIST }, { "vlist", TOK_VLIST }, { NULL, 0 } }; static struct _s_x otypes[] = { { "EACTION", IPFW_TLV_EACTION }, { "DYNSTATE", IPFW_TLV_STATE_NAME }, { NULL, 0 } }; static const char* lookup_eaction_name(ipfw_obj_ntlv *ntlv, int cnt, uint16_t type) { const char *name; int i; name = NULL; for (i = 0; i < cnt; i++) { if (ntlv[i].head.type != IPFW_TLV_EACTION) continue; if (IPFW_TLV_EACTION_NAME(ntlv[i].idx) != type) continue; name = ntlv[i].name; break; } return (name); } static void ipfw_list_objects(int ac, char *av[]) { ipfw_obj_lheader req, *olh; ipfw_obj_ntlv *ntlv; const char *name; size_t sz; int i; memset(&req, 0, sizeof(req)); sz = sizeof(req); if (do_get3(IP_FW_DUMP_SRVOBJECTS, &req.opheader, &sz) != 0) if (errno != ENOMEM) return; sz = req.size; if ((olh = calloc(1, sz)) == NULL) return; olh->size = sz; if (do_get3(IP_FW_DUMP_SRVOBJECTS, &olh->opheader, &sz) != 0) { free(olh); return; } if (olh->count > 0) printf("Objects list:\n"); else printf("There are no objects\n"); ntlv = (ipfw_obj_ntlv *)(olh + 1); for (i = 0; i < olh->count; i++) { name = match_value(otypes, ntlv->head.type); if (name == NULL) name = lookup_eaction_name( (ipfw_obj_ntlv *)(olh + 1), olh->count, ntlv->head.type); if (name == NULL) printf(" kidx: %4d\ttype: %10d\tname: %s\n", ntlv->idx, ntlv->head.type, ntlv->name); else printf(" kidx: %4d\ttype: %10s\tname: %s\n", ntlv->idx, name, ntlv->name); ntlv++; } free(olh); } void ipfw_internal_handler(int ac, char *av[]) { int tcmd; ac--; av++; NEED1("internal cmd required"); if ((tcmd = match_token(intcmds, *av)) == -1) errx(EX_USAGE, "invalid internal sub-cmd: %s", *av); switch (tcmd) { case TOK_IFLIST: ipfw_list_tifaces(); break; case TOK_TALIST: ipfw_list_ta(ac, av); break; case TOK_OLIST: ipfw_list_objects(ac, av); break; case TOK_VLIST: ipfw_list_values(ac, av); break; } } static int ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh) { ipfw_obj_lheader req, *olh; size_t sz; memset(&req, 0, sizeof(req)); sz = sizeof(req); if (do_get3(IP_FW_XIFLIST, &req.opheader, &sz) != 0) { if (errno != ENOMEM) return (errno); } sz = req.size; if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_XIFLIST, &olh->opheader, &sz) != 0) { free(olh); return (errno); } *polh = olh; return (0); } static int ifinfo_cmp(const void *a, const void *b) { ipfw_iface_info *ia, *ib; ia = (ipfw_iface_info *)a; ib = (ipfw_iface_info *)b; return (stringnum_cmp(ia->ifname, ib->ifname)); } /* * Retrieves table list from kernel, * optionally sorts it and calls requested function for each table. * Returns 0 on success. */ static void ipfw_list_tifaces() { ipfw_obj_lheader *olh; ipfw_iface_info *info; int i, error; if ((error = ipfw_get_tracked_ifaces(&olh)) != 0) err(EX_OSERR, "Unable to request ipfw tracked interface list"); qsort(olh + 1, olh->count, olh->objsize, ifinfo_cmp); info = (ipfw_iface_info *)(olh + 1); for (i = 0; i < olh->count; i++) { if (info->flags & IPFW_IFFLAG_RESOLVED) printf("%s ifindex: %d refcount: %u changes: %u\n", info->ifname, info->ifindex, info->refcnt, info->gencnt); else printf("%s ifindex: unresolved refcount: %u changes: %u\n", info->ifname, info->refcnt, info->gencnt); info = (ipfw_iface_info *)((caddr_t)info + olh->objsize); } free(olh); } Index: projects/clang700-import/sbin/ping/ping.c =================================================================== --- projects/clang700-import/sbin/ping/ping.c (revision 341812) +++ projects/clang700-import/sbin/ping/ping.c (revision 341813) @@ -1,1814 +1,1814 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Muuss. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); /* * P I N G . C * * Using the Internet Control Message Protocol (ICMP) "ECHO" facility, * measure round-trip-delays and packet loss across network paths. * * Author - * Mike Muuss * U. S. Army Ballistic Research Laboratory * December, 1983 * * Status - * Public Domain. Distribution Unlimited. * Bugs - * More statistics could always be gathered. * This program has to run SUID to ROOT to access the ICMP socket. */ #include /* NB: we rely on this for */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IPSEC #include #endif /*IPSEC*/ #include #include #include #include #include #include #include #include #include #include #include #define INADDR_LEN ((int)sizeof(in_addr_t)) #define TIMEVAL_LEN ((int)sizeof(struct tv32)) #define MASK_LEN (ICMP_MASKLEN - ICMP_MINLEN) #define TS_LEN (ICMP_TSLEN - ICMP_MINLEN) #define DEFDATALEN 56 /* default data length */ #define FLOOD_BACKOFF 20000 /* usecs to back off if F_FLOOD mode */ /* runs out of buffer space */ #define MAXIPLEN (sizeof(struct ip) + MAX_IPOPTLEN) #define MAXICMPLEN (ICMP_ADVLENMIN + MAX_IPOPTLEN) #define MAXWAIT 10000 /* max ms to wait for response */ #define MAXALARM (60 * 60) /* max seconds for alarm timeout */ #define MAXTOS 255 #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ #define SET(bit) (A(bit) |= B(bit)) #define CLR(bit) (A(bit) &= (~B(bit))) #define TST(bit) (A(bit) & B(bit)) struct tv32 { int32_t tv32_sec; int32_t tv32_usec; }; /* various options */ static int options; #define F_FLOOD 0x0001 #define F_INTERVAL 0x0002 #define F_NUMERIC 0x0004 #define F_PINGFILLED 0x0008 #define F_QUIET 0x0010 #define F_RROUTE 0x0020 #define F_SO_DEBUG 0x0040 #define F_SO_DONTROUTE 0x0080 #define F_VERBOSE 0x0100 #define F_QUIET2 0x0200 #define F_NOLOOP 0x0400 #define F_MTTL 0x0800 #define F_MIF 0x1000 #define F_AUDIBLE 0x2000 #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC #define F_POLICY 0x4000 #endif /*IPSEC_POLICY_IPSEC*/ #endif /*IPSEC*/ #define F_TTL 0x8000 #define F_MISSED 0x10000 #define F_ONCE 0x20000 #define F_HDRINCL 0x40000 #define F_MASK 0x80000 #define F_TIME 0x100000 #define F_SWEEP 0x200000 #define F_WAITTIME 0x400000 /* * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum * number of received sequence numbers we can keep track of. Change 128 * to 8192 for complete accuracy... */ #define MAX_DUP_CHK (8 * 128) static int mx_dup_ck = MAX_DUP_CHK; static char rcvd_tbl[MAX_DUP_CHK / 8]; static struct sockaddr_in whereto; /* who to ping */ static int datalen = DEFDATALEN; static int maxpayload; static int ssend; /* send socket file descriptor */ static int srecv; /* receive socket file descriptor */ static u_char outpackhdr[IP_MAXPACKET], *outpack; static char BBELL = '\a'; /* characters written for MISSED and AUDIBLE */ static char BSPACE = '\b'; /* characters written for flood */ static char DOT = '.'; static char *hostname; static char *shostname; static int ident; /* process id to identify our packets */ static int uid; /* cached uid for micro-optimization */ static u_char icmp_type = ICMP_ECHO; static u_char icmp_type_rsp = ICMP_ECHOREPLY; static int phdr_len = 0; static int send_len; /* counters */ static long nmissedmax; /* max value of ntransmitted - nreceived - 1 */ static long npackets; /* max packets to transmit */ static long nreceived; /* # of packets we got back */ static long nrepeats; /* number of duplicates */ static long ntransmitted; /* sequence # for outbound packets = #sent */ static long snpackets; /* max packets to transmit in one sweep */ static long sntransmitted; /* # of packets we sent in this sweep */ static int sweepmax; /* max value of payload in sweep */ static int sweepmin = 0; /* start value of payload in sweep */ static int sweepincr = 1; /* payload increment in sweep */ static int interval = 1000; /* interval between packets, ms */ static int waittime = MAXWAIT; /* timeout for each packet */ static long nrcvtimeout = 0; /* # of packets we got back after waittime */ /* timing */ static int timing; /* flag to do timing */ static double tmin = 999999999.0; /* minimum round trip time */ static double tmax = 0.0; /* maximum round trip time */ static double tsum = 0.0; /* sum of all times, for doing average */ static double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ /* nonzero if we've been told to finish up */ static volatile sig_atomic_t finish_up; static volatile sig_atomic_t siginfo_p; static cap_channel_t *capdns; static void fill(char *, char *); static u_short in_cksum(u_short *, int); static cap_channel_t *capdns_setup(void); static void check_status(void); static void finish(void) __dead2; static void pinger(void); static char *pr_addr(struct in_addr); static char *pr_ntime(n_time); static void pr_icmph(struct icmp *); static void pr_iph(struct ip *); static void pr_pack(char *, int, struct sockaddr_in *, struct timeval *); static void pr_retip(struct ip *); static void status(int); static void stopit(int); static void tvsub(struct timeval *, const struct timeval *); static void usage(void) __dead2; int main(int argc, char *const *argv) { struct sockaddr_in from, sock_in; struct in_addr ifaddr; struct timeval last, intvl; struct iovec iov; struct ip *ip; struct msghdr msg; struct sigaction si_sa; size_t sz; u_char *datap, packet[IP_MAXPACKET] __aligned(4); char *ep, *source, *target, *payload; struct hostent *hp; #ifdef IPSEC_POLICY_IPSEC char *policy_in, *policy_out; #endif struct sockaddr_in *to; double t; u_long alarmtimeout; long ltmp; int almost_done, ch, df, hold, i, icmp_len, mib[4], preload; int ssend_errno, srecv_errno, tos, ttl; char ctrl[CMSG_SPACE(sizeof(struct timeval))]; char hnamebuf[MAXHOSTNAMELEN], snamebuf[MAXHOSTNAMELEN]; #ifdef IP_OPTIONS char rspace[MAX_IPOPTLEN]; /* record route space */ #endif unsigned char loop, mttl; payload = source = NULL; #ifdef IPSEC_POLICY_IPSEC policy_in = policy_out = NULL; #endif cap_rights_t rights; bool cansandbox; /* * Do the stuff that we need root priv's for *first*, and * then drop our setuid bit. Save error reporting for * after arg parsing. * * Historicaly ping was using one socket 's' for sending and for * receiving. After capsicum(4) related changes we use two * sockets. It was done for special ping use case - when user * issue ping on multicast or broadcast address replies come * from different addresses, not from the address we * connect(2)'ed to, and send socket do not receive those * packets. */ ssend = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); ssend_errno = errno; srecv = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); srecv_errno = errno; if (setuid(getuid()) != 0) err(EX_NOPERM, "setuid() failed"); uid = getuid(); if (ssend < 0) { errno = ssend_errno; err(EX_OSERR, "ssend socket"); } if (srecv < 0) { errno = srecv_errno; err(EX_OSERR, "srecv socket"); } alarmtimeout = df = preload = tos = 0; outpack = outpackhdr + sizeof(struct ip); while ((ch = getopt(argc, argv, "Aac:DdfG:g:h:I:i:Ll:M:m:nop:QqRrS:s:T:t:vW:z:" #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC "P:" #endif /*IPSEC_POLICY_IPSEC*/ #endif /*IPSEC*/ )) != -1) { switch(ch) { case 'A': options |= F_MISSED; break; case 'a': options |= F_AUDIBLE; break; case 'c': ltmp = strtol(optarg, &ep, 0); - if (*ep || ep == optarg || ltmp > LONG_MAX || ltmp <=0) + if (*ep || ep == optarg || ltmp <= 0) errx(EX_USAGE, "invalid count of packets to transmit: `%s'", optarg); npackets = ltmp; break; case 'D': options |= F_HDRINCL; df = 1; break; case 'd': options |= F_SO_DEBUG; break; case 'f': if (uid) { errno = EPERM; err(EX_NOPERM, "-f flag"); } options |= F_FLOOD; setbuf(stdout, (char *)NULL); break; case 'G': /* Maximum packet size for ping sweep */ ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp <= 0) errx(EX_USAGE, "invalid packet size: `%s'", optarg); if (uid != 0 && ltmp > DEFDATALEN) { errno = EPERM; err(EX_NOPERM, "packet size too large: %ld > %u", ltmp, DEFDATALEN); } options |= F_SWEEP; sweepmax = ltmp; break; case 'g': /* Minimum packet size for ping sweep */ ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp <= 0) errx(EX_USAGE, "invalid packet size: `%s'", optarg); if (uid != 0 && ltmp > DEFDATALEN) { errno = EPERM; err(EX_NOPERM, "packet size too large: %ld > %u", ltmp, DEFDATALEN); } options |= F_SWEEP; sweepmin = ltmp; break; case 'h': /* Packet size increment for ping sweep */ ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp < 1) errx(EX_USAGE, "invalid increment size: `%s'", optarg); if (uid != 0 && ltmp > DEFDATALEN) { errno = EPERM; err(EX_NOPERM, "packet size too large: %ld > %u", ltmp, DEFDATALEN); } options |= F_SWEEP; sweepincr = ltmp; break; case 'I': /* multicast interface */ if (inet_aton(optarg, &ifaddr) == 0) errx(EX_USAGE, "invalid multicast interface: `%s'", optarg); options |= F_MIF; break; case 'i': /* wait between sending packets */ t = strtod(optarg, &ep) * 1000.0; if (*ep || ep == optarg || t > (double)INT_MAX) errx(EX_USAGE, "invalid timing interval: `%s'", optarg); options |= F_INTERVAL; interval = (int)t; if (uid && interval < 1000) { errno = EPERM; err(EX_NOPERM, "-i interval too short"); } break; case 'L': options |= F_NOLOOP; loop = 0; break; case 'l': ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp > INT_MAX || ltmp < 0) errx(EX_USAGE, "invalid preload value: `%s'", optarg); if (uid) { errno = EPERM; err(EX_NOPERM, "-l flag"); } preload = ltmp; break; case 'M': switch(optarg[0]) { case 'M': case 'm': options |= F_MASK; break; case 'T': case 't': options |= F_TIME; break; default: errx(EX_USAGE, "invalid message: `%c'", optarg[0]); break; } break; case 'm': /* TTL */ ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp > MAXTTL || ltmp < 0) errx(EX_USAGE, "invalid TTL: `%s'", optarg); ttl = ltmp; options |= F_TTL; break; case 'n': options |= F_NUMERIC; break; case 'o': options |= F_ONCE; break; #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC case 'P': options |= F_POLICY; if (!strncmp("in", optarg, 2)) policy_in = strdup(optarg); else if (!strncmp("out", optarg, 3)) policy_out = strdup(optarg); else errx(1, "invalid security policy"); break; #endif /*IPSEC_POLICY_IPSEC*/ #endif /*IPSEC*/ case 'p': /* fill buffer with user pattern */ options |= F_PINGFILLED; payload = optarg; break; case 'Q': options |= F_QUIET2; break; case 'q': options |= F_QUIET; break; case 'R': options |= F_RROUTE; break; case 'r': options |= F_SO_DONTROUTE; break; case 'S': source = optarg; break; case 's': /* size of packet to send */ ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp < 0) errx(EX_USAGE, "invalid packet size: `%s'", optarg); if (uid != 0 && ltmp > DEFDATALEN) { errno = EPERM; err(EX_NOPERM, "packet size too large: %ld > %u", ltmp, DEFDATALEN); } datalen = ltmp; break; case 'T': /* multicast TTL */ ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp > MAXTTL || ltmp < 0) errx(EX_USAGE, "invalid multicast TTL: `%s'", optarg); mttl = ltmp; options |= F_MTTL; break; case 't': alarmtimeout = strtoul(optarg, &ep, 0); if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX)) errx(EX_USAGE, "invalid timeout: `%s'", optarg); if (alarmtimeout > MAXALARM) errx(EX_USAGE, "invalid timeout: `%s' > %d", optarg, MAXALARM); alarm((int)alarmtimeout); break; case 'v': options |= F_VERBOSE; break; case 'W': /* wait ms for answer */ t = strtod(optarg, &ep); if (*ep || ep == optarg || t > (double)INT_MAX) errx(EX_USAGE, "invalid timing interval: `%s'", optarg); options |= F_WAITTIME; waittime = (int)t; break; case 'z': options |= F_HDRINCL; ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp > MAXTOS || ltmp < 0) errx(EX_USAGE, "invalid TOS: `%s'", optarg); tos = ltmp; break; default: usage(); } } if (argc - optind != 1) usage(); target = argv[optind]; switch (options & (F_MASK|F_TIME)) { case 0: break; case F_MASK: icmp_type = ICMP_MASKREQ; icmp_type_rsp = ICMP_MASKREPLY; phdr_len = MASK_LEN; if (!(options & F_QUIET)) (void)printf("ICMP_MASKREQ\n"); break; case F_TIME: icmp_type = ICMP_TSTAMP; icmp_type_rsp = ICMP_TSTAMPREPLY; phdr_len = TS_LEN; if (!(options & F_QUIET)) (void)printf("ICMP_TSTAMP\n"); break; default: errx(EX_USAGE, "ICMP_TSTAMP and ICMP_MASKREQ are exclusive."); break; } icmp_len = sizeof(struct ip) + ICMP_MINLEN + phdr_len; if (options & F_RROUTE) icmp_len += MAX_IPOPTLEN; maxpayload = IP_MAXPACKET - icmp_len; if (datalen > maxpayload) errx(EX_USAGE, "packet size too large: %d > %d", datalen, maxpayload); send_len = icmp_len + datalen; datap = &outpack[ICMP_MINLEN + phdr_len + TIMEVAL_LEN]; if (options & F_PINGFILLED) { fill((char *)datap, payload); } capdns = capdns_setup(); if (source) { bzero((char *)&sock_in, sizeof(sock_in)); sock_in.sin_family = AF_INET; if (inet_aton(source, &sock_in.sin_addr) != 0) { shostname = source; } else { hp = cap_gethostbyname2(capdns, source, AF_INET); if (!hp) errx(EX_NOHOST, "cannot resolve %s: %s", source, hstrerror(h_errno)); sock_in.sin_len = sizeof sock_in; if ((unsigned)hp->h_length > sizeof(sock_in.sin_addr) || hp->h_length < 0) errx(1, "gethostbyname2: illegal address"); memcpy(&sock_in.sin_addr, hp->h_addr_list[0], sizeof(sock_in.sin_addr)); (void)strncpy(snamebuf, hp->h_name, sizeof(snamebuf) - 1); snamebuf[sizeof(snamebuf) - 1] = '\0'; shostname = snamebuf; } if (bind(ssend, (struct sockaddr *)&sock_in, sizeof sock_in) == -1) err(1, "bind"); } bzero(&whereto, sizeof(whereto)); to = &whereto; to->sin_family = AF_INET; to->sin_len = sizeof *to; if (inet_aton(target, &to->sin_addr) != 0) { hostname = target; } else { hp = cap_gethostbyname2(capdns, target, AF_INET); if (!hp) errx(EX_NOHOST, "cannot resolve %s: %s", target, hstrerror(h_errno)); if ((unsigned)hp->h_length > sizeof(to->sin_addr)) errx(1, "gethostbyname2 returned an illegal address"); memcpy(&to->sin_addr, hp->h_addr_list[0], sizeof to->sin_addr); (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); hnamebuf[sizeof(hnamebuf) - 1] = '\0'; hostname = hnamebuf; } /* From now on we will use only reverse DNS lookups. */ if (capdns != NULL) { const char *types[1]; types[0] = "ADDR2NAME"; if (cap_dns_type_limit(capdns, types, 1) < 0) err(1, "unable to limit access to system.dns service"); } if (connect(ssend, (struct sockaddr *)&whereto, sizeof(whereto)) != 0) err(1, "connect"); if (options & F_FLOOD && options & F_INTERVAL) errx(EX_USAGE, "-f and -i: incompatible options"); if (options & F_FLOOD && IN_MULTICAST(ntohl(to->sin_addr.s_addr))) errx(EX_USAGE, "-f flag cannot be used with multicast destination"); if (options & (F_MIF | F_NOLOOP | F_MTTL) && !IN_MULTICAST(ntohl(to->sin_addr.s_addr))) errx(EX_USAGE, "-I, -L, -T flags cannot be used with unicast destination"); if (datalen >= TIMEVAL_LEN) /* can we time transfer */ timing = 1; if (!(options & F_PINGFILLED)) for (i = TIMEVAL_LEN; i < datalen; ++i) *datap++ = i; ident = getpid() & 0xFFFF; hold = 1; if (options & F_SO_DEBUG) { (void)setsockopt(ssend, SOL_SOCKET, SO_DEBUG, (char *)&hold, sizeof(hold)); (void)setsockopt(srecv, SOL_SOCKET, SO_DEBUG, (char *)&hold, sizeof(hold)); } if (options & F_SO_DONTROUTE) (void)setsockopt(ssend, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, sizeof(hold)); #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC if (options & F_POLICY) { char *buf; if (policy_in != NULL) { buf = ipsec_set_policy(policy_in, strlen(policy_in)); if (buf == NULL) errx(EX_CONFIG, "%s", ipsec_strerror()); if (setsockopt(srecv, IPPROTO_IP, IP_IPSEC_POLICY, buf, ipsec_get_policylen(buf)) < 0) err(EX_CONFIG, "ipsec policy cannot be configured"); free(buf); } if (policy_out != NULL) { buf = ipsec_set_policy(policy_out, strlen(policy_out)); if (buf == NULL) errx(EX_CONFIG, "%s", ipsec_strerror()); if (setsockopt(ssend, IPPROTO_IP, IP_IPSEC_POLICY, buf, ipsec_get_policylen(buf)) < 0) err(EX_CONFIG, "ipsec policy cannot be configured"); free(buf); } } #endif /*IPSEC_POLICY_IPSEC*/ #endif /*IPSEC*/ if (options & F_HDRINCL) { ip = (struct ip*)outpackhdr; if (!(options & (F_TTL | F_MTTL))) { mib[0] = CTL_NET; mib[1] = PF_INET; mib[2] = IPPROTO_IP; mib[3] = IPCTL_DEFTTL; sz = sizeof(ttl); if (sysctl(mib, 4, &ttl, &sz, NULL, 0) == -1) err(1, "sysctl(net.inet.ip.ttl)"); } setsockopt(ssend, IPPROTO_IP, IP_HDRINCL, &hold, sizeof(hold)); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(struct ip) >> 2; ip->ip_tos = tos; ip->ip_id = 0; ip->ip_off = htons(df ? IP_DF : 0); ip->ip_ttl = ttl; ip->ip_p = IPPROTO_ICMP; ip->ip_src.s_addr = source ? sock_in.sin_addr.s_addr : INADDR_ANY; ip->ip_dst = to->sin_addr; } if (options & F_NUMERIC) cansandbox = true; else if (capdns != NULL) cansandbox = CASPER_SUPPORT; else cansandbox = false; /* * Here we enter capability mode. Further down access to global * namespaces (e.g filesystem) is restricted (see capsicum(4)). * We must connect(2) our socket before this point. */ if (cansandbox && cap_enter() < 0 && errno != ENOSYS) err(1, "cap_enter"); cap_rights_init(&rights, CAP_RECV, CAP_EVENT, CAP_SETSOCKOPT); if (cap_rights_limit(srecv, &rights) < 0 && errno != ENOSYS) err(1, "cap_rights_limit srecv"); cap_rights_init(&rights, CAP_SEND, CAP_SETSOCKOPT); if (cap_rights_limit(ssend, &rights) < 0 && errno != ENOSYS) err(1, "cap_rights_limit ssend"); /* record route option */ if (options & F_RROUTE) { #ifdef IP_OPTIONS bzero(rspace, sizeof(rspace)); rspace[IPOPT_OPTVAL] = IPOPT_RR; rspace[IPOPT_OLEN] = sizeof(rspace) - 1; rspace[IPOPT_OFFSET] = IPOPT_MINOFF; rspace[sizeof(rspace) - 1] = IPOPT_EOL; if (setsockopt(ssend, IPPROTO_IP, IP_OPTIONS, rspace, sizeof(rspace)) < 0) err(EX_OSERR, "setsockopt IP_OPTIONS"); #else errx(EX_UNAVAILABLE, "record route not available in this implementation"); #endif /* IP_OPTIONS */ } if (options & F_TTL) { if (setsockopt(ssend, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) { err(EX_OSERR, "setsockopt IP_TTL"); } } if (options & F_NOLOOP) { if (setsockopt(ssend, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) { err(EX_OSERR, "setsockopt IP_MULTICAST_LOOP"); } } if (options & F_MTTL) { if (setsockopt(ssend, IPPROTO_IP, IP_MULTICAST_TTL, &mttl, sizeof(mttl)) < 0) { err(EX_OSERR, "setsockopt IP_MULTICAST_TTL"); } } if (options & F_MIF) { if (setsockopt(ssend, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr)) < 0) { err(EX_OSERR, "setsockopt IP_MULTICAST_IF"); } } #ifdef SO_TIMESTAMP { int on = 1; if (setsockopt(srecv, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0) err(EX_OSERR, "setsockopt SO_TIMESTAMP"); } #endif if (sweepmax) { if (sweepmin > sweepmax) errx(EX_USAGE, "Maximum packet size must be no less than the minimum packet size"); if (datalen != DEFDATALEN) errx(EX_USAGE, "Packet size and ping sweep are mutually exclusive"); if (npackets > 0) { snpackets = npackets; npackets = 0; } else snpackets = 1; datalen = sweepmin; send_len = icmp_len + sweepmin; } if (options & F_SWEEP && !sweepmax) errx(EX_USAGE, "Maximum sweep size must be specified"); /* * When pinging the broadcast address, you can get a lot of answers. * Doing something so evil is useful if you are trying to stress the * ethernet, or just want to fill the arp cache to get some stuff for * /etc/ethers. But beware: RFC 1122 allows hosts to ignore broadcast * or multicast pings if they wish. */ /* * XXX receive buffer needs undetermined space for mbuf overhead * as well. */ hold = IP_MAXPACKET + 128; (void)setsockopt(srecv, SOL_SOCKET, SO_RCVBUF, (char *)&hold, sizeof(hold)); /* CAP_SETSOCKOPT removed */ cap_rights_init(&rights, CAP_RECV, CAP_EVENT); if (cap_rights_limit(srecv, &rights) < 0 && errno != ENOSYS) err(1, "cap_rights_limit srecv setsockopt"); if (uid == 0) (void)setsockopt(ssend, SOL_SOCKET, SO_SNDBUF, (char *)&hold, sizeof(hold)); /* CAP_SETSOCKOPT removed */ cap_rights_init(&rights, CAP_SEND); if (cap_rights_limit(ssend, &rights) < 0 && errno != ENOSYS) err(1, "cap_rights_limit ssend setsockopt"); if (to->sin_family == AF_INET) { (void)printf("PING %s (%s)", hostname, inet_ntoa(to->sin_addr)); if (source) (void)printf(" from %s", shostname); if (sweepmax) (void)printf(": (%d ... %d) data bytes\n", sweepmin, sweepmax); else (void)printf(": %d data bytes\n", datalen); } else { if (sweepmax) (void)printf("PING %s: (%d ... %d) data bytes\n", hostname, sweepmin, sweepmax); else (void)printf("PING %s: %d data bytes\n", hostname, datalen); } /* * Use sigaction() instead of signal() to get unambiguous semantics, * in particular with SA_RESTART not set. */ sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; si_sa.sa_handler = stopit; if (sigaction(SIGINT, &si_sa, 0) == -1) { err(EX_OSERR, "sigaction SIGINT"); } si_sa.sa_handler = status; if (sigaction(SIGINFO, &si_sa, 0) == -1) { err(EX_OSERR, "sigaction"); } if (alarmtimeout > 0) { si_sa.sa_handler = stopit; if (sigaction(SIGALRM, &si_sa, 0) == -1) err(EX_OSERR, "sigaction SIGALRM"); } bzero(&msg, sizeof(msg)); msg.msg_name = (caddr_t)&from; msg.msg_iov = &iov; msg.msg_iovlen = 1; #ifdef SO_TIMESTAMP msg.msg_control = (caddr_t)ctrl; #endif iov.iov_base = packet; iov.iov_len = IP_MAXPACKET; if (preload == 0) pinger(); /* send the first ping */ else { if (npackets != 0 && preload > npackets) preload = npackets; while (preload--) /* fire off them quickies */ pinger(); } (void)gettimeofday(&last, NULL); if (options & F_FLOOD) { intvl.tv_sec = 0; intvl.tv_usec = 10000; } else { intvl.tv_sec = interval / 1000; intvl.tv_usec = interval % 1000 * 1000; } almost_done = 0; while (!finish_up) { struct timeval now, timeout; fd_set rfds; int cc, n; check_status(); if ((unsigned)srecv >= FD_SETSIZE) errx(EX_OSERR, "descriptor too large"); FD_ZERO(&rfds); FD_SET(srecv, &rfds); (void)gettimeofday(&now, NULL); timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec; timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec; while (timeout.tv_usec < 0) { timeout.tv_usec += 1000000; timeout.tv_sec--; } while (timeout.tv_usec >= 1000000) { timeout.tv_usec -= 1000000; timeout.tv_sec++; } if (timeout.tv_sec < 0) timerclear(&timeout); n = select(srecv + 1, &rfds, NULL, NULL, &timeout); if (n < 0) continue; /* Must be EINTR. */ if (n == 1) { struct timeval *tv = NULL; #ifdef SO_TIMESTAMP struct cmsghdr *cmsg = (struct cmsghdr *)&ctrl; msg.msg_controllen = sizeof(ctrl); #endif msg.msg_namelen = sizeof(from); if ((cc = recvmsg(srecv, &msg, 0)) < 0) { if (errno == EINTR) continue; warn("recvmsg"); continue; } #ifdef SO_TIMESTAMP if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP && cmsg->cmsg_len == CMSG_LEN(sizeof *tv)) { /* Copy to avoid alignment problems: */ memcpy(&now, CMSG_DATA(cmsg), sizeof(now)); tv = &now; } #endif if (tv == NULL) { (void)gettimeofday(&now, NULL); tv = &now; } pr_pack((char *)packet, cc, &from, tv); if ((options & F_ONCE && nreceived) || (npackets && nreceived >= npackets)) break; } if (n == 0 || options & F_FLOOD) { if (sweepmax && sntransmitted == snpackets) { for (i = 0; i < sweepincr ; ++i) *datap++ = i; datalen += sweepincr; if (datalen > sweepmax) break; send_len = icmp_len + datalen; sntransmitted = 0; } if (!npackets || ntransmitted < npackets) pinger(); else { if (almost_done) break; almost_done = 1; intvl.tv_usec = 0; if (nreceived) { intvl.tv_sec = 2 * tmax / 1000; if (!intvl.tv_sec) intvl.tv_sec = 1; } else { intvl.tv_sec = waittime / 1000; intvl.tv_usec = waittime % 1000 * 1000; } } (void)gettimeofday(&last, NULL); if (ntransmitted - nreceived - 1 > nmissedmax) { nmissedmax = ntransmitted - nreceived - 1; if (options & F_MISSED) (void)write(STDOUT_FILENO, &BBELL, 1); } } } finish(); /* NOTREACHED */ exit(0); /* Make the compiler happy */ } /* * stopit -- * Set the global bit that causes the main loop to quit. * Do NOT call finish() from here, since finish() does far too much * to be called from a signal handler. */ void stopit(int sig __unused) { /* * When doing reverse DNS lookups, the finish_up flag might not * be noticed for a while. Just exit if we get a second SIGINT. */ if (!(options & F_NUMERIC) && finish_up) _exit(nreceived ? 0 : 2); finish_up = 1; } /* * pinger -- * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet * will be added on by the kernel. The ID field is our UNIX process ID, * and the sequence number is an ascending integer. The first TIMEVAL_LEN * bytes of the data portion are used to hold a UNIX "timeval" struct in * host byte-order, to compute the round-trip time. */ static void pinger(void) { struct timeval now; struct tv32 tv32; struct ip *ip; struct icmp *icp; int cc, i; u_char *packet; packet = outpack; icp = (struct icmp *)outpack; icp->icmp_type = icmp_type; icp->icmp_code = 0; icp->icmp_cksum = 0; icp->icmp_seq = htons(ntransmitted); icp->icmp_id = ident; /* ID */ CLR(ntransmitted % mx_dup_ck); if ((options & F_TIME) || timing) { (void)gettimeofday(&now, NULL); tv32.tv32_sec = htonl(now.tv_sec); tv32.tv32_usec = htonl(now.tv_usec); if (options & F_TIME) icp->icmp_otime = htonl((now.tv_sec % (24*60*60)) * 1000 + now.tv_usec / 1000); if (timing) bcopy((void *)&tv32, (void *)&outpack[ICMP_MINLEN + phdr_len], sizeof(tv32)); } cc = ICMP_MINLEN + phdr_len + datalen; /* compute ICMP checksum here */ icp->icmp_cksum = in_cksum((u_short *)icp, cc); if (options & F_HDRINCL) { cc += sizeof(struct ip); ip = (struct ip *)outpackhdr; ip->ip_len = htons(cc); ip->ip_sum = in_cksum((u_short *)outpackhdr, cc); packet = outpackhdr; } i = send(ssend, (char *)packet, cc, 0); if (i < 0 || i != cc) { if (i < 0) { if (options & F_FLOOD && errno == ENOBUFS) { usleep(FLOOD_BACKOFF); return; } warn("sendto"); } else { warn("%s: partial write: %d of %d bytes", hostname, i, cc); } } ntransmitted++; sntransmitted++; if (!(options & F_QUIET) && options & F_FLOOD) (void)write(STDOUT_FILENO, &DOT, 1); } /* * pr_pack -- * Print out the packet, if it came from us. This logic is necessary * because ALL readers of the ICMP socket get a copy of ALL ICMP packets * which arrive ('tis only fair). This permits multiple copies of this * program to be run without having intermingled output (or statistics!). */ static void pr_pack(char *buf, int cc, struct sockaddr_in *from, struct timeval *tv) { struct in_addr ina; u_char *cp, *dp; struct icmp *icp; struct ip *ip; const void *tp; double triptime; int dupflag, hlen, i, j, recv_len, seq; static int old_rrlen; static char old_rr[MAX_IPOPTLEN]; /* Check the IP header */ ip = (struct ip *)buf; hlen = ip->ip_hl << 2; recv_len = cc; if (cc < hlen + ICMP_MINLEN) { if (options & F_VERBOSE) warn("packet too short (%d bytes) from %s", cc, inet_ntoa(from->sin_addr)); return; } /* Now the ICMP part */ cc -= hlen; icp = (struct icmp *)(buf + hlen); if (icp->icmp_type == icmp_type_rsp) { if (icp->icmp_id != ident) return; /* 'Twas not our ECHO */ ++nreceived; triptime = 0.0; if (timing) { struct timeval tv1; struct tv32 tv32; #ifndef icmp_data tp = &icp->icmp_ip; #else tp = icp->icmp_data; #endif tp = (const char *)tp + phdr_len; if ((size_t)(cc - ICMP_MINLEN - phdr_len) >= sizeof(tv1)) { /* Copy to avoid alignment problems: */ memcpy(&tv32, tp, sizeof(tv32)); tv1.tv_sec = ntohl(tv32.tv32_sec); tv1.tv_usec = ntohl(tv32.tv32_usec); tvsub(tv, &tv1); triptime = ((double)tv->tv_sec) * 1000.0 + ((double)tv->tv_usec) / 1000.0; tsum += triptime; tsumsq += triptime * triptime; if (triptime < tmin) tmin = triptime; if (triptime > tmax) tmax = triptime; } else timing = 0; } seq = ntohs(icp->icmp_seq); if (TST(seq % mx_dup_ck)) { ++nrepeats; --nreceived; dupflag = 1; } else { SET(seq % mx_dup_ck); dupflag = 0; } if (options & F_QUIET) return; if (options & F_WAITTIME && triptime > waittime) { ++nrcvtimeout; return; } if (options & F_FLOOD) (void)write(STDOUT_FILENO, &BSPACE, 1); else { (void)printf("%d bytes from %s: icmp_seq=%u", cc, inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), seq); (void)printf(" ttl=%d", ip->ip_ttl); if (timing) (void)printf(" time=%.3f ms", triptime); if (dupflag) (void)printf(" (DUP!)"); if (options & F_AUDIBLE) (void)write(STDOUT_FILENO, &BBELL, 1); if (options & F_MASK) { /* Just prentend this cast isn't ugly */ (void)printf(" mask=%s", inet_ntoa(*(struct in_addr *)&(icp->icmp_mask))); } if (options & F_TIME) { (void)printf(" tso=%s", pr_ntime(icp->icmp_otime)); (void)printf(" tsr=%s", pr_ntime(icp->icmp_rtime)); (void)printf(" tst=%s", pr_ntime(icp->icmp_ttime)); } if (recv_len != send_len) { (void)printf( "\nwrong total length %d instead of %d", recv_len, send_len); } /* check the data */ cp = (u_char*)&icp->icmp_data[phdr_len]; dp = &outpack[ICMP_MINLEN + phdr_len]; cc -= ICMP_MINLEN + phdr_len; i = 0; if (timing) { /* don't check variable timestamp */ cp += TIMEVAL_LEN; dp += TIMEVAL_LEN; cc -= TIMEVAL_LEN; i += TIMEVAL_LEN; } for (; i < datalen && cc > 0; ++i, ++cp, ++dp, --cc) { if (*cp != *dp) { (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp); (void)printf("\ncp:"); cp = (u_char*)&icp->icmp_data[0]; for (i = 0; i < datalen; ++i, ++cp) { if ((i % 16) == 8) (void)printf("\n\t"); (void)printf("%2x ", *cp); } (void)printf("\ndp:"); cp = &outpack[ICMP_MINLEN]; for (i = 0; i < datalen; ++i, ++cp) { if ((i % 16) == 8) (void)printf("\n\t"); (void)printf("%2x ", *cp); } break; } } } } else { /* * We've got something other than an ECHOREPLY. * See if it's a reply to something that we sent. * We can compare IP destination, protocol, * and ICMP type and ID. * * Only print all the error messages if we are running * as root to avoid leaking information not normally * available to those not running as root. */ #ifndef icmp_data struct ip *oip = &icp->icmp_ip; #else struct ip *oip = (struct ip *)icp->icmp_data; #endif struct icmp *oicmp = (struct icmp *)(oip + 1); if (((options & F_VERBOSE) && uid == 0) || (!(options & F_QUIET2) && (oip->ip_dst.s_addr == whereto.sin_addr.s_addr) && (oip->ip_p == IPPROTO_ICMP) && (oicmp->icmp_type == ICMP_ECHO) && (oicmp->icmp_id == ident))) { (void)printf("%d bytes from %s: ", cc, pr_addr(from->sin_addr)); pr_icmph(icp); } else return; } /* Display any IP options */ cp = (u_char *)buf + sizeof(struct ip); for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) switch (*cp) { case IPOPT_EOL: hlen = 0; break; case IPOPT_LSRR: case IPOPT_SSRR: (void)printf(*cp == IPOPT_LSRR ? "\nLSRR: " : "\nSSRR: "); j = cp[IPOPT_OLEN] - IPOPT_MINOFF + 1; hlen -= 2; cp += 2; if (j >= INADDR_LEN && j <= hlen - (int)sizeof(struct ip)) { for (;;) { bcopy(++cp, &ina.s_addr, INADDR_LEN); if (ina.s_addr == 0) (void)printf("\t0.0.0.0"); else (void)printf("\t%s", pr_addr(ina)); hlen -= INADDR_LEN; cp += INADDR_LEN - 1; j -= INADDR_LEN; if (j < INADDR_LEN) break; (void)putchar('\n'); } } else (void)printf("\t(truncated route)\n"); break; case IPOPT_RR: j = cp[IPOPT_OLEN]; /* get length */ i = cp[IPOPT_OFFSET]; /* and pointer */ hlen -= 2; cp += 2; if (i > j) i = j; i = i - IPOPT_MINOFF + 1; if (i < 0 || i > (hlen - (int)sizeof(struct ip))) { old_rrlen = 0; continue; } if (i == old_rrlen && !bcmp((char *)cp, old_rr, i) && !(options & F_FLOOD)) { (void)printf("\t(same route)"); hlen -= i; cp += i; break; } old_rrlen = i; bcopy((char *)cp, old_rr, i); (void)printf("\nRR: "); if (i >= INADDR_LEN && i <= hlen - (int)sizeof(struct ip)) { for (;;) { bcopy(++cp, &ina.s_addr, INADDR_LEN); if (ina.s_addr == 0) (void)printf("\t0.0.0.0"); else (void)printf("\t%s", pr_addr(ina)); hlen -= INADDR_LEN; cp += INADDR_LEN - 1; i -= INADDR_LEN; if (i < INADDR_LEN) break; (void)putchar('\n'); } } else (void)printf("\t(truncated route)"); break; case IPOPT_NOP: (void)printf("\nNOP"); break; default: (void)printf("\nunknown option %x", *cp); break; } if (!(options & F_FLOOD)) { (void)putchar('\n'); (void)fflush(stdout); } } /* * in_cksum -- * Checksum routine for Internet Protocol family headers (C Version) */ u_short in_cksum(u_short *addr, int len) { int nleft, sum; u_short *w; union { u_short us; u_char uc[2]; } last; u_short answer; nleft = len; sum = 0; w = addr; /* * Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { last.uc[0] = *(u_char *)w; last.uc[1] = 0; sum += last.us; } /* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return(answer); } /* * tvsub -- * Subtract 2 timeval structs: out = out - in. Out is assumed to * be >= in. */ static void tvsub(struct timeval *out, const struct timeval *in) { if ((out->tv_usec -= in->tv_usec) < 0) { --out->tv_sec; out->tv_usec += 1000000; } out->tv_sec -= in->tv_sec; } /* * status -- * Print out statistics when SIGINFO is received. */ static void status(int sig __unused) { siginfo_p = 1; } static void check_status(void) { if (siginfo_p) { siginfo_p = 0; (void)fprintf(stderr, "\r%ld/%ld packets received (%.1f%%)", nreceived, ntransmitted, ntransmitted ? nreceived * 100.0 / ntransmitted : 0.0); if (nreceived && timing) (void)fprintf(stderr, " %.3f min / %.3f avg / %.3f max", tmin, tsum / (nreceived + nrepeats), tmax); (void)fprintf(stderr, "\n"); } } /* * finish -- * Print out statistics, and give up. */ static void finish(void) { (void)signal(SIGINT, SIG_IGN); (void)signal(SIGALRM, SIG_IGN); (void)putchar('\n'); (void)fflush(stdout); (void)printf("--- %s ping statistics ---\n", hostname); (void)printf("%ld packets transmitted, ", ntransmitted); (void)printf("%ld packets received, ", nreceived); if (nrepeats) (void)printf("+%ld duplicates, ", nrepeats); if (ntransmitted) { if (nreceived > ntransmitted) (void)printf("-- somebody's printing up packets!"); else (void)printf("%.1f%% packet loss", ((ntransmitted - nreceived) * 100.0) / ntransmitted); } if (nrcvtimeout) (void)printf(", %ld packets out of wait time", nrcvtimeout); (void)putchar('\n'); if (nreceived && timing) { double n = nreceived + nrepeats; double avg = tsum / n; double vari = tsumsq / n - avg * avg; (void)printf( "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n", tmin, avg, tmax, sqrt(vari)); } if (nreceived) exit(0); else exit(2); } #ifdef notdef static char *ttab[] = { "Echo Reply", /* ip + seq + udata */ "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ "Source Quench", /* IP */ "Redirect", /* redirect type, gateway, + IP */ "Echo", "Time Exceeded", /* transit, frag reassem + IP */ "Parameter Problem", /* pointer + IP */ "Timestamp", /* id + seq + three timestamps */ "Timestamp Reply", /* " */ "Info Request", /* id + sq */ "Info Reply" /* " */ }; #endif /* * pr_icmph -- * Print a descriptive string about an ICMP header. */ static void pr_icmph(struct icmp *icp) { switch(icp->icmp_type) { case ICMP_ECHOREPLY: (void)printf("Echo Reply\n"); /* XXX ID + Seq + Data */ break; case ICMP_UNREACH: switch(icp->icmp_code) { case ICMP_UNREACH_NET: (void)printf("Destination Net Unreachable\n"); break; case ICMP_UNREACH_HOST: (void)printf("Destination Host Unreachable\n"); break; case ICMP_UNREACH_PROTOCOL: (void)printf("Destination Protocol Unreachable\n"); break; case ICMP_UNREACH_PORT: (void)printf("Destination Port Unreachable\n"); break; case ICMP_UNREACH_NEEDFRAG: (void)printf("frag needed and DF set (MTU %d)\n", ntohs(icp->icmp_nextmtu)); break; case ICMP_UNREACH_SRCFAIL: (void)printf("Source Route Failed\n"); break; case ICMP_UNREACH_FILTER_PROHIB: (void)printf("Communication prohibited by filter\n"); break; default: (void)printf("Dest Unreachable, Bad Code: %d\n", icp->icmp_code); break; } /* Print returned IP header information */ #ifndef icmp_data pr_retip(&icp->icmp_ip); #else pr_retip((struct ip *)icp->icmp_data); #endif break; case ICMP_SOURCEQUENCH: (void)printf("Source Quench\n"); #ifndef icmp_data pr_retip(&icp->icmp_ip); #else pr_retip((struct ip *)icp->icmp_data); #endif break; case ICMP_REDIRECT: switch(icp->icmp_code) { case ICMP_REDIRECT_NET: (void)printf("Redirect Network"); break; case ICMP_REDIRECT_HOST: (void)printf("Redirect Host"); break; case ICMP_REDIRECT_TOSNET: (void)printf("Redirect Type of Service and Network"); break; case ICMP_REDIRECT_TOSHOST: (void)printf("Redirect Type of Service and Host"); break; default: (void)printf("Redirect, Bad Code: %d", icp->icmp_code); break; } (void)printf("(New addr: %s)\n", inet_ntoa(icp->icmp_gwaddr)); #ifndef icmp_data pr_retip(&icp->icmp_ip); #else pr_retip((struct ip *)icp->icmp_data); #endif break; case ICMP_ECHO: (void)printf("Echo Request\n"); /* XXX ID + Seq + Data */ break; case ICMP_TIMXCEED: switch(icp->icmp_code) { case ICMP_TIMXCEED_INTRANS: (void)printf("Time to live exceeded\n"); break; case ICMP_TIMXCEED_REASS: (void)printf("Frag reassembly time exceeded\n"); break; default: (void)printf("Time exceeded, Bad Code: %d\n", icp->icmp_code); break; } #ifndef icmp_data pr_retip(&icp->icmp_ip); #else pr_retip((struct ip *)icp->icmp_data); #endif break; case ICMP_PARAMPROB: (void)printf("Parameter problem: pointer = 0x%02x\n", icp->icmp_hun.ih_pptr); #ifndef icmp_data pr_retip(&icp->icmp_ip); #else pr_retip((struct ip *)icp->icmp_data); #endif break; case ICMP_TSTAMP: (void)printf("Timestamp\n"); /* XXX ID + Seq + 3 timestamps */ break; case ICMP_TSTAMPREPLY: (void)printf("Timestamp Reply\n"); /* XXX ID + Seq + 3 timestamps */ break; case ICMP_IREQ: (void)printf("Information Request\n"); /* XXX ID + Seq */ break; case ICMP_IREQREPLY: (void)printf("Information Reply\n"); /* XXX ID + Seq */ break; case ICMP_MASKREQ: (void)printf("Address Mask Request\n"); break; case ICMP_MASKREPLY: (void)printf("Address Mask Reply\n"); break; case ICMP_ROUTERADVERT: (void)printf("Router Advertisement\n"); break; case ICMP_ROUTERSOLICIT: (void)printf("Router Solicitation\n"); break; default: (void)printf("Bad ICMP type: %d\n", icp->icmp_type); } } /* * pr_iph -- * Print an IP header with options. */ static void pr_iph(struct ip *ip) { struct in_addr ina; u_char *cp; int hlen; hlen = ip->ip_hl << 2; cp = (u_char *)ip + 20; /* point to options */ (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst\n"); (void)printf(" %1x %1x %02x %04x %04x", ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len), ntohs(ip->ip_id)); (void)printf(" %1lx %04lx", (u_long) (ntohl(ip->ip_off) & 0xe000) >> 13, (u_long) ntohl(ip->ip_off) & 0x1fff); (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ntohs(ip->ip_sum)); memcpy(&ina, &ip->ip_src.s_addr, sizeof ina); (void)printf(" %s ", inet_ntoa(ina)); memcpy(&ina, &ip->ip_dst.s_addr, sizeof ina); (void)printf(" %s ", inet_ntoa(ina)); /* dump any option bytes */ while (hlen-- > 20) { (void)printf("%02x", *cp++); } (void)putchar('\n'); } /* * pr_addr -- * Return an ascii host address as a dotted quad and optionally with * a hostname. */ static char * pr_addr(struct in_addr ina) { struct hostent *hp; static char buf[16 + 3 + MAXHOSTNAMELEN]; if (options & F_NUMERIC) return inet_ntoa(ina); hp = cap_gethostbyaddr(capdns, (char *)&ina, 4, AF_INET); if (hp == NULL) return inet_ntoa(ina); (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name, inet_ntoa(ina)); return(buf); } /* * pr_retip -- * Dump some info on a returned (via ICMP) IP packet. */ static void pr_retip(struct ip *ip) { u_char *cp; int hlen; pr_iph(ip); hlen = ip->ip_hl << 2; cp = (u_char *)ip + hlen; if (ip->ip_p == 6) (void)printf("TCP: from port %u, to port %u (decimal)\n", (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); else if (ip->ip_p == 17) (void)printf("UDP: from port %u, to port %u (decimal)\n", (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); } static char * pr_ntime(n_time timestamp) { static char buf[10]; int hour, min, sec; sec = ntohl(timestamp) / 1000; hour = sec / 60 / 60; min = (sec % (60 * 60)) / 60; sec = (sec % (60 * 60)) % 60; (void)snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hour, min, sec); return (buf); } static void fill(char *bp, char *patp) { char *cp; int pat[16]; u_int ii, jj, kk; for (cp = patp; *cp; cp++) { if (!isxdigit(*cp)) errx(EX_USAGE, "patterns must be specified as hex digits"); } ii = sscanf(patp, "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], &pat[13], &pat[14], &pat[15]); if (ii > 0) for (kk = 0; kk <= maxpayload - (TIMEVAL_LEN + ii); kk += ii) for (jj = 0; jj < ii; ++jj) bp[jj + kk] = pat[jj]; if (!(options & F_QUIET)) { (void)printf("PATTERN: 0x"); for (jj = 0; jj < ii; ++jj) (void)printf("%02x", bp[jj] & 0xFF); (void)printf("\n"); } } static cap_channel_t * capdns_setup(void) { cap_channel_t *capcas, *capdnsloc; const char *types[2]; int families[1]; capcas = cap_init(); if (capcas == NULL) err(1, "unable to create casper process"); capdnsloc = cap_service_open(capcas, "system.dns"); /* Casper capability no longer needed. */ cap_close(capcas); if (capdnsloc == NULL) err(1, "unable to open system.dns service"); types[0] = "NAME2ADDR"; types[1] = "ADDR2NAME"; if (cap_dns_type_limit(capdnsloc, types, 2) < 0) err(1, "unable to limit access to system.dns service"); families[0] = AF_INET; if (cap_dns_family_limit(capdnsloc, families, 1) < 0) err(1, "unable to limit access to system.dns service"); return (capdnsloc); } #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) #define SECOPT " [-P policy]" #else #define SECOPT "" #endif static void usage(void) { (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", "usage: ping [-AaDdfnoQqRrv] [-c count] [-G sweepmaxsize] [-g sweepminsize]", " [-h sweepincrsize] [-i wait] [-l preload] [-M mask | time] [-m ttl]", " " SECOPT " [-p pattern] [-S src_addr] [-s packetsize] [-t timeout]", " [-W waittime] [-z tos] host", " ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait] [-l preload]", " [-M mask | time] [-m ttl]" SECOPT " [-p pattern] [-S src_addr]", " [-s packetsize] [-T ttl] [-t timeout] [-W waittime]", " [-z tos] mcast-group"); exit(EX_USAGE); } Index: projects/clang700-import/share/vt/keymaps/INDEX.keymaps =================================================================== --- projects/clang700-import/share/vt/keymaps/INDEX.keymaps (revision 341812) +++ projects/clang700-import/share/vt/keymaps/INDEX.keymaps (revision 341813) @@ -1,599 +1,605 @@ # $FreeBSD$ # # database for kbdmap(8) # # Format :: # # lang: ar bg cs da de el en es fi fr he hr hu hy is it ja kk ko nl no pl # pt ro ru sh sk sl sv tr uk zh # lang: lang,lang # # If lang empty use 'en' (us-english) as default. # # Example: # german.iso.kbd:de:deutsch # german.iso.kbd:en:german # # See also setlocal # /usr/share/locale, /usr/local/lib/X11/locale/locale.alias # # # Language support: MENU, FONT # MENU:en:Choose your keyboard layout MENU:da,no,sv:Vælg dit keyboard layout MENU:de:Wählen Sie Ihre Tastaturbelegung MENU:fr:Choisissez la disposition de votre clavier MENU:pl:Wybierz układ klawiatury MENU:pt:Escolha o layout do teclado MENU:es:Seleccione la disposición de su teclado MENU:he:ךלש תדלקמה תפש תא רחב MENU:uk:Оберiть розкладку клавіатури MENU:el:Επιλέξτε το πληκτρολόγιο της κονσόλας MENU:hy:Ընտրեք ստեղնաշարի դասավորությունը MENU:tr:Klavye düzeninizi seçiniz # # The font definition for "en" is the fall-back font for # all languages. # Add language specific font definitions only where required! # FONT:en:vgarom-8x16.hex # be.kbd:en:Belgian be.kbd:de:Belgisch be.kbd:fr:Belge be.kbd:pt,es:Belga be.acc.kbd:en:Belgian (accent keys) be.acc.kbd:de:Belgisch (mit Akzenten) be.acc.kbd:fr:Belge (avec accents) be.acc.kbd:pt:Belga (com acentos) be.acc.kbd:es:Belga (con acentos) bg.bds.kbd:en:Bulgarian (BDS) bg.bds.kbd:de:Bulgarisch (BDS) bg.phonetic.kbd:en:Bulgarian (Phonetic) bg.phonetic.kbd:de:Bulgarisch (phonetisch) br.kbd:en:Brazilian (accent keys) br.kbd:de:Brasilianisch (mit Akzenten) br.kbd:fr:Brésilien (avec accents) br.kbd:pt:Brasileiro (com acentos) br.kbd:es:Brasileño (con acentos) br.noacc.kbd:en:Brazilian (without accent keys) br.noacc.kbd:de:Brasilianisch (ohne Akzente) br.noacc.kbd:fr:Brésilien (sans accents) br.noacc.kbd:pt:Brasileiro (without accent keys) br.noacc.kbd:es:Brasileño (without accent keys) by.kbd:en:Belarusian by.kbd:de:Weißrussisch by.kbd:fr:Biélorusse centraleuropean.kbd:en:Central European centraleuropean.kbd:de:Zentral Europäisch centraleuropean.kbd:fr:Centre européen centraleuropean.kbd:es:Centroeuropeo centraleuropean.qwerty.kbd:en:Central European (QWERTY) centraleuropean.qwerty.kbd:de:Zentral Europäisch (QWERTY) centraleuropean.qwerty.kbd:fr:Centre européen (QWERTY) centraleuropean.qwerty.kbd:es:Centroeuropeo (QWERTY) colemak.acc.kbd:en:Colemak ergonomic alternative cz.kbd:en:Czech (QWERTZ, accent keys) cz.kbd:de:Tschechisch (QWERTZ, mit Akzenten) cz.kbd:fr:Tchèque (QWERTZ, avec accents) cz.kbd:es:Checo (QWERTZ, con acentos) cz.kbd.from-ce:en:Czech cz.kbd.from-ce:de:Tschechisch cz.kbd.from-ce:fr:Tchèque cz.kbd.from-ce:es:Checo cz.qwerty.kbd.from-ce:en:Czech (QWERTY) cz.qwerty.kbd.from-ce:de:Tschechisch (QWERTY) cz.qwerty.kbd.from-ce:fr:Tchèquey (QWERTY) cz.qwerty.kbd.from-ce:es:Checo (QWERTY) dk.kbd:en:Danish dk.kbd:da:Dansk dk.kbd:de:Dänisch dk.kbd:fr:Danois dk.kbd:pt:Dinamarquês dk.kbd:es:Danés dk.acc.kbd:en:Danish (accent keys) dk.acc.kbd:da:Dansk (accent taster) dk.acc.kbd:de:Dänisch (mit Akzenten) dk.acc.kbd:fr:Danois (avec accents) dk.acc.kbd:pt:Dinamarquês (com acentos) dk.acc.kbd:es:Danés (con acentos) dk.kbd.from-cp865:en:Danish dk.kbd.from-cp865:da:Dansk dk.kbd.from-cp865:de:Dänisch dk.kbd.from-cp865:fr:Danois dk.kbd.from-cp865:pt:Dinamarquês dk.kbd.from-cp865:es:Danés dk.macbook.kbd:en:Danish (macbook) dk.macbook.kbd:da:Dansk (macbook) dk.macbook.kbd:de:Dänisch (Macbook) dk.macbook.kbd:fr:Danois (macbook) dk.macbook.kbd:pt:Dinamarquês (macbook) dk.macbook.kbd:es:Danés (macbook) nl.kbd:en:Dutch (accent keys) nl.kbd:de:Holländisch (mit Akzenten) nordic.asus-eee.kbd:en:Nordic layout on Asus eeePC nordic.asus-eee.kbd:fr:Norvégien phonétique sur Asus eeePC gr.kbd:en:Greek (104 keys) gr.kbd:fr:Grec (104 touches) gr.kbd:el:Ελληνικό (104 πλήκτρων) ee.kbd.from-iso1:en:Estonian ee.kbd.from-iso1:de:Estnisch ee.kbd.from-iso1:fr:Estonien ee.kbd.from-iso1:es:Estonio ee.kbd:en:Estonian ee.kbd:de:Estnisch ee.kbd:fr:Estonien ee.kbd:es:Estonio ee.kbd.from-cp850:en:Estonian ee.kbd.from-cp850:de:Estnisch ee.kbd.from-cp850:fr:Estonien ee.kbd.from-cp850:es:Estonio fi.kbd:en:Finnish fi.kbd:de:Finnisch fi.kbd:fr:Finlandais fi.kbd:es:Finlandés fi.kbd.from-cp850:en:Finnish fi.kbd.from-cp850:de:Finnisch fi.kbd.from-cp850:fr:Finlandais fi.kbd.from-cp850:es:Finlandés fr.kbd:en:French fr.kbd:de:Französisch fr.kbd:fr:Français fr.kbd:pt:Francês fr.kbd:es:Francés fr.kbd:uk:Французька fr.acc.kbd:en:French (accent keys) fr.acc.kbd:de:Französisch (mit Akzenten) fr.acc.kbd:fr:Français (avec accents) fr.acc.kbd:pt:Francês (com acentos) fr.acc.kbd:es:Francés (con acentos) fr.acc.kbd:uk:Французька (accent keys) fr.macbook.kbd:en:French Macbook/Macbook Pro (accent keys) fr.macbook.kbd:de:Französisch Macbook/Macbook Pro (mit Aksenten) fr.macbook.kbd:fr:Français Macbook/Macbook Pro (accent keys) fr.macbook.kbd:pt:Francês Macbook/Macbook Pro (com acentos) fr.macbook.kbd:es:Francés Macbook/Macbook Pro (con acentos) fr.dvorak.kbd:en:French Dvorak-like fr.dvorak.kbd:de:Französisch Dvorak fr.dvorak.kbd:fr:Français Dvorak fr.dvorak.kbd:pt:Francês Dvorak fr.dvorak.kbd:es:Francés Dvorak fr.dvorak.kbd:uk:French Dvorak-like fr.dvorak.acc.kbd:en:French Dvorak-like (accent keys) fr.dvorak.acc.kbd:de:Französisch Dvorak (mit Akzenten) fr.dvorak.acc.kbd:fr:Français Dvorak (accent keys) fr.dvorak.acc.kbd:pt:Francês Dvorak (com acentos) fr.dvorak.acc.kbd:es:Francés Dvorak (con acentos) fr.dvorak.acc.kbd:uk:French Dvorak-like (accent keys) ca.kbd:en:Canadian Bilingual ca-fr.kbd:en:French Canadian (accent keys) ca-fr.kbd:de:Französisch Kanada (mit Akzenten) ca-fr.kbd:fr:Français Canadien (avec accents) ca-fr.kbd:es:Francocanadiense (con acentos) ca-fr.kbd:uk:Французько-канадська (accent keys) de.kbd:en:German de.kbd:de:Deutsch de.kbd:fr:Allemand de.kbd:pt:Alemão de.kbd:es:Alemán de.kbd:tr:Almanca de.kbd:uk:Німецька de.acc.kbd:en:German (accent keys) de.acc.kbd:de:Deutsch (mit Akzenten) de.acc.kbd:fr:Allemand (avec accents) de.acc.kbd:pt:Alemão (com acentos) de.acc.kbd:es:Alemán (con acentos) de.acc.kbd:uk:Німецька (accent keys) de.noacc.kbd:en:German (no accent keys) de.noacc.kbd:de:Deutsch (ohne Akzente) de.noacc.kbd:fr:Allemand (sans accents) de.noacc.kbd:pt:Alemão (no accent keys) de.noacc.kbd:es:Alemán (no accent keys) de.noacc.kbd:uk:Німецька (no accent keys) de.kbd.from-cp850:en:German de.kbd.from-cp850:de:Deutsch de.kbd.from-cp850:fr:Allemand de.kbd.from-cp850:pt:Alemão de.kbd.from-cp850:es:Alemán de.kbd.from-cp850:uk:Німецька gr.elot.acc.kbd:en:Greek ELOT gr.elot.acc.kbd:de:Grieschisch ELOT gr.elot.acc.kbd:fr:Grec ELOT gr.elot.acc.kbd:el:Ελληνικό ΕΛΟΤ gr.101.acc.kbd:en:Greek (101 keys) gr.101.acc.kbd:de:Grieschisch (101 Tasten) gr.101.acc.kbd:fr:Grec (101 touches) gr.101.acc.kbd:el:Ελληνικό (101 πλήκτρων) il.kbd:en:Hebrew il.kbd:de:Hebräisch il.kbd:fr:Hébreu il.kbd::תירבע hr.kbd:en:Croatian hr.kbd:de:Kroatisch hr.kbd:fr:Croate hr.kbd:es:Croata hu.101.kbd:en:Hungarian (101 keys) hu.101.kbd:de:Ungarisch (101 Tasten) hu.101.kbd:fr:Hongrois (101 touches) hu.101.kbd:es:Húngaro (101) hu.102.kbd:en:Hungarian (102 keys) hu.102.kbd:de:Ungarisch (102 Tasten) hu.102.kbd:fr:Hongrois (102 touches) hu.102.kbd:es:Húngaro (102) am.kbd:hy:Հայերեն հնչյունային (Phonetic) դասավորություն am.kbd:ru:Армянская фонетическая раскладка am.kbd:en:Armenian phonetic layout am.kbd:fr:Arménien phonétique am.kbd:de:Armenische phonetische Tastenbelegung is.kbd:en:Icelandic is.kbd:de:Isländisch is.kbd:fr:Islandais is.kbd:pt:Islandês is.kbd:es:Islandés is.acc.kbd:en:Icelandic (accent keys) is.acc.kbd:de:Isländisch (mit Akzenten) is.acc.kbd:fr:Islandais (avec accents) is.acc.kbd:pt:Islandês (com acentos) is.acc.kbd:es:Islandés (con acentos) it.kbd:en:Italian it.kbd:de:Italienisch it.kbd:fr:Italien it.kbd:pt,es:Italiano it.kbd:uk:Італійська jp.kbd:en:Japanese 106 jp.kbd:de:Japanisch 106 jp.kbd:fr:Japonais 106 jp.kbd:pt:Japonês 106 jp.kbd:es:Japonés 106 jp.kbd:uk:Японська 106 jp.capsctrl.kbd:en:Japanese 106x jp.capsctrl.kbd:de:Japanisch 106x jp.capsctrl.kbd:fr:Japonais 106x jp.capsctrl.kbd:pt:Japonês 106x jp.capsctrl.kbd:es:Japonés 106x jp.capsctrl.kbd:uk:Японська 106x kz.kst.kbd:en:Kazakh kz.kst.kbd:de:Kasachisch kz.kst.kbd:fr:Kazakh kz.io.kbd:en:Kazakh kz.io.kbd:de:Kasachisch kz.io.kbd:fr:Kazakh latinamerican.kbd:en:Latin American latinamerican.kbd:de:Latein Amerikanisch latinamerican.kbd:fr:Amérique latine latinamerican.kbd:pt,es:América Latina latinamerican.acc.kbd:en:Latin American (accent keys) latinamerican.acc.kbd:de:Latein Amerikanisch (mit Akzenten) latinamerican.acc.kbd:fr:Amérique latine (avec accents) latinamerican.acc.kbd:pt,es:América Latina (com acentos) lt.kbd:en:Lithuanian lt.kbd:de:Litauisch lt.kbd:fr:Lithuanien lt.kbd:es:Lituano no.kbd:en:Norwegian no.kbd:no:Norsk no.kbd:de:Norwegisch no.kbd:fr:Norvégien no.kbd:pt:Norueguês no.kbd:es:Noruego no.dvorak.kbd:en:Norwegian dvorak no.dvorak.kbd:no:Norsk dvorak no.dvorak.kbd:de:Norwegisch dvorak no.dvorak.kbd:fr:Norvégien dvorak no.dvorak.kbd:pt:Norueguês dvorak no.dvorak.kbd:es:Noruego Idvorak pl.kbd:en:Polish (programmer's) pl.kbd:de:Polnisch (für Programmierer) pl.kbd:fr:Polonais (pour programmeurs) pl.kbd:pl:Polska (programisty) pl.kbd:pt:Polonês (para programadores) pl.kbd:es:Polaco (para programadores) pl.dvorak.kbd:en:Polish Dvorak pl.dvorak.kbd:de:Polnisch Dvorak pl.dvorak.kbd:fr:Polonais Dvorak pl.dvorak.kbd:pl:Polska Dvorak pl.dvorak.kbd:pt:Polonês Dvorak pl.dvorak.kbd:es:Polaco Dvorak pt.kbd:en:Portuguese pt.kbd:de:Portugisisch pt.kbd:fr:Portugais pt.kbd:pt:Português pt.kbd:es:Portugués pt.acc.kbd:en:Portuguese (accent keys) pt.acc.kbd:de:Portugisisch (mit Akzenten) pt.acc.kbd:fr:Portugais (avec accents) pt.acc.kbd:pt:Português (com acentos) pt.acc.kbd:es:Portugués (con acentos) ru.kbd.from-cp866:en:Russian (alternative) ru.kbd.from-cp866:de:Russisch (alternativ) ru.kbd.from-cp866:ru:Русский (alternative) ru.kbd.from-cp866:fr:Russe (alternative) ru.kbd.from-cp866:pt:Russo (alternativo) ru.kbd.from-cp866:es:Ruso (alternativo) ru.kbd.from-cp866:uk:Російська (альтернативна) ru.kbd.from-iso5:en:Russian ru.kbd.from-iso5:de:Russisch ru.kbd.from-iso5:fr:Russe ru.kbd.from-iso5:ru:Русский ru.kbd.from-iso5:pt:Russo ru.kbd.from-iso5:es:Ruso ru.kbd.from-iso5:uk:Російський ru.kbd:en:Russian ru.kbd:de:Russisch ru.kbd:ru:Русский ru.kbd:fr:Russe ru.kbd:pt:Russo ru.kbd:es:Ruso ru.kbd:uk:Російська ru.shift.kbd:en:Russian (shift) ru.shift.kbd:de:Russisch (shift) ru.shift.kbd:ru:Русский (shift) ru.shift.kbd:fr:Russe (shift) ru.shift.kbd:pt:Russo (shift) ru.shift.kbd:es:Ruso (shift) ru.shift.kbd:uk:Російська (shift) ru.win.kbd:en:Russian (winkeys) ru.win.kbd:de:Russisch (winkeys) ru.win.kbd:ru:Русский (winkeys) ru.win.kbd:fr:Russe (winkeys) ru.win.kbd:pt:Russo (winkeys) ru.win.kbd:es:Ruso (winkeys) ru.win.kbd:uk:Російська (winkeys) es.dvorak.kbd:en:Spanish Dvorak es.dvorak.kbd:de:Spanisch Dvorak es.dvorak.kbd:fr:Espagnol Dvorak es.dvorak.kbd:pt:Espanhol Dvorak es.dvorak.kbd:es:Español Dvorak es.kbd.from-iso1:en:Spanish es.kbd.from-iso1:de:Spanisch es.kbd.from-iso1:fr:Espagnol es.kbd.from-iso1:pt:Espanhol es.kbd.from-iso1:es:Español es.acc.kbd:en:Spanish (accent keys) es.acc.kbd:de:Spanisch (accent keys) es.acc.kbd:fr:Espagnol (avec accents) es.acc.kbd:pt:Espanhol (com acentos) es.acc.kbd:es:Español (con acentos) es.kbd:en:Spanish (accent keys) es.kbd:de:Spanisch (accent keys) es.kbd:fr:Espagnol (avec accents) es.kbd:pt:Espanhol (com acentos) es.kbd:es:Español (con acentos) si.kbd:en:Slovenian si.kbd:de:Slovenisch si.kbd:fr:Slovène si.kbd:pt:Eslovênio si.kbd:es:Esloveno sk.kbd:en:Slovak sk.kbd:de:Slovakisch sk.kbd:fr:Slovène sk.kbd:es:Eslovaco se.kbd:en:Swedish se.kbd:de:Schwedisch se.kbd:fr:Suédois se.kbd:pt,es:Sueco se.kbd.from-cp850:en:Swedish se.kbd.from-cp850:de:Schwedisch se.kbd.from-cp850:fr:Suédois se.kbd.from-cp850:pt,es:Sueco ch-fr.kbd:en:Swiss-French ch-fr.kbd:de:Schweiz-Französisch ch-fr.kbd:fr:Suisse-Français ch-fr.kbd:es:Francosuizo ch-fr.acc.kbd:en:Swiss-French (accent keys) ch-fr.acc.kbd:de:Schweiz-Französisch (mit Akzenten) ch-fr.acc.kbd:fr:Suisse-Français (avec accents) ch-fr.acc.kbd:es:Francosuizo (con acentos) ch-fr.kbd.from-cp850:en:Swiss-French ch-fr.kbd.from-cp850:de:Schweiz-Französisch ch-fr.kbd.from-cp850:fr:Suisse-Français ch-fr.kbd.from-cp850:es:Francosuizo ch.kbd:en:Swiss-German ch.kbd:de:Schweiz-Deutsch ch.kbd:fr:Suisse-Allemand ch.kbd:pt:Suiço-Alemão ch.kbd:es:Germanosuizo ch.acc.kbd:en:Swiss-German (accent keys) ch.acc.kbd:de:Schweiz-Deutsch (mit Akzenten) ch.acc.kbd:fr:Suisse-Allemand (avec accents) ch.acc.kbd:pt:Suiço-Alemão (com acentos) ch.acc.kbd:es:Germanosuizo (con acentos) ch.kbd.from-cp850:en:Swiss-German ch.kbd.from-cp850:de:Schweiz-Deutsch ch.kbd.from-cp850:fr:Suisse-Allemand ch.kbd.from-cp850:pt:Suiço-Alemão ch.kbd.from-cp850:es:Germanosuizo ch.macbook.acc.kbd:en:Swiss-German Macbook/Macbook Pro (accent keys) ch.macbook.acc.kbd:de:Schweiz-Deutsch Macbook/Macbook Pro (mit Akzenten) ch.macbook.acc.kbd:fr:Suisse-Allemand Macbook/Macbook Pro (avec accents) ch.macbook.acc.kbd:pt:Suiço-Alemão Macbook/Macbook Pro (com acentos) ch.macbook.acc.kbd:es:Germanosuizo Macbook/Macbook Pro (con acentos) tr.kbd:en:Turkish (Q) tr.kbd:de:Türkisch (Q) tr.kbd:fr:Turc (Q) tr.kbd:uk:Турецька (Q) tr.kbd:tr:Türkçe (Q) tr.f.kbd:en:Turkish (F) tr.f.kbd:de:Türkisch (F) tr.f.kbd:fr:Turc (F) tr.f.kbd:uk:Турецька (F) tr.f.kbd:tr:Türkçe (F) uk.kbd:en:United Kingdom uk.kbd:de:Vereinigtes Königreich uk.kbd:fr:Royaume Uni uk.kbd:pt:Reino Unido uk.kbd:es:Británico uk.capsctrl.kbd:en:United Kingdom (Caps Lock acts as Left Ctrl) uk.capsctrl.kbd:de:Vereinigtes Königreich (Caps Lock als linke Strg) #uk.capsctrl.kbd:fr:Royaume Uni (caps lock acts as Left Ctrl) #uk.capsctrl.kbd:pt:Reino Unido (caps lock acts as Left Ctrl) #uk.capsctrl.kbd:es:Británico (caps lock acts as Left Ctrl) uk.dvorak.kbd:en:United Kingdom Dvorak uk.dvorak.kbd:de:Vereinigtes Königreich Dvorak uk.dvorak.kbd:fr:Royaume Uni Dvorak uk.dvorak.kbd:pt:Reino Unido Dvorak uk.dvorak.kbd:es:Británico Dvorak +uk.macbook.kbd:en:United Kingdom Macbook +uk.macbook.kbd:de:Vereinigtes Königreich Macbook +uk.macbook.kbd:fr:Royaume Uni Macbook +uk.macbook.kbd:pt:Reino Unido Macbook +uk.macbook.kbd:es:Británico Macbook + us.kbd:en:United States of America us.kbd:de:US-amerikanisch us.kbd:fr:États Unis d'Amérique us.kbd:pt:Estados Unidos da América us.kbd:es:Estadounidense us.acc.kbd:en:United States of America (accent keys) us.acc.kbd:de:US-amerikanisch (mit Akzenten) us.acc.kbd:fr:États Unis d'Amérique (avec accents) us.acc.kbd:pt:Estados Unidos da América (com acentos) us.acc.kbd:es:Estadounidense (con acentos) us.dvorak.kbd:en:United States of America dvorak us.dvorak.kbd:de:US-amerikanisch dvorak us.dvorak.kbd:fr:États Unis d'Amérique dvorak us.dvorak.kbd:pt:Estados Unidos da América dvorak us.dvorak.kbd:es:Estadounidense dvorak us.dvorakr.kbd:en:United States of America righthand dvorak us.dvorakr.kbd:de:US-amerikanisch dvorak rechte Hand us.dvorakr.kbd:fr:États Unis d'Amérique dvorakr us.dvorakr.kbd:pt:Estados Unidos da América dvorakr us.dvorakr.kbd:es:Estadounidense dvorak diestro us.dvorakl.kbd:en:United States of America lefthand dvorak us.dvorakl.kbd:de:US-amerikanisch dvorak linke Hand us.dvorakl.kbd:fr:États Unis d'Amérique dvorakl us.dvorakl.kbd:pt:Estados Unidos da América dvorakl us.dvorakl.kbd:es:Estadounidense dvorak zurdo us.dvorakp.kbd:en:United States of America Programmer Dvorak us.dvorakp.kbd:de:US-amerikanisch (Dvorak für Programmierer) us.dvorakp.kbd:fr:États Unis d'Amérique dvorakp us.dvorakp.kbd:pt:Estados Unidos da América dvorakp us.dvorakp.kbd:es:Estadounidense dvorakp us.dvorakx.kbd:en:United States of America dvorakx us.dvorakx.kbd:de:US-amerikanisch dvorakx us.dvorakx.kbd:fr:États Unis d'Amérique dvorakx us.dvorakx.kbd:pt:Estados Unidos da América dvorakx us.dvorakx.kbd:es:Estadounidense dvorakx us.emacs.kbd:en:United States of America Emacs optimized layout us.emacs.kbd:de:US-amerikanisch für Emacs optimiert us.emacs.kbd:fr:États Unis d'Amérique emacs us.emacs.kbd:pt:Estados Unidos da América emacs us.emacs.kbd:es:Estadounidense optimizado para Emacs us.ctrl.kbd:en:United States of America (Caps Lock acts as Left Ctrl) us.unix.kbd:en:United States of America Traditional Unix Workstation us.unix.kbd:de:US-amerikanisch traditionelles Unix Layout us.unix.kbd:fr:États Unis d'Amérique unix us.unix.kbd:pt:Estados Unidos da América unix us.unix.kbd:es:Estadounidense Unix tradicional us.macbook.kbd:en:United States of America Macbook/Macbook Pro ISO-8859-1 us.macbook.kbd:de:US-amerikanisch Macbook/Macbook Pro ISO-8859-1 us.macbook.kbd:fr:États Unis d'Amérique Macbook / Macbook Pro ISO-8859-1 us.macbook.kbd:pt:Estados Unidos da América Macbook/Macbook Pro ISO-8859-1 us.macbook.kbd:es:Estadounidense Macbook/Macbook Pro ISO-8859-1 ua.kbd.from-iso5:en:Ukrainian ua.kbd.from-iso5:de:Ukrainisch ua.kbd.from-iso5:fr:Ukrainien ua.kbd.from-iso5:ru:Украинский ua.kbd.from-iso5:uk:Українська ua.kbd:en:Ukrainian ua.kbd:de:Ukrainisch ua.kbd:fr:Ukrainien ua.kbd:uk:Українська ua.shift.alt.kbd:en:Ukrainian (with Russian) (shift) ua.shift.alt.kbd:de:Ukrainisch (mit Russisch) (shift) ua.shift.alt.kbd:fr:Ukrainien (koi8-u avec koi8-r) (shift) ua.shift.alt.kbd:uk:Українська (koi8-u з) koi8-r (shift) Index: projects/clang700-import/share/vt/keymaps/Makefile =================================================================== --- projects/clang700-import/share/vt/keymaps/Makefile (revision 341812) +++ projects/clang700-import/share/vt/keymaps/Makefile (revision 341813) @@ -1,91 +1,92 @@ # $FreeBSD$ FILES= INDEX.keymaps \ am.kbd \ be.acc.kbd \ be.kbd \ bg.bds.kbd \ bg.phonetic.kbd \ br.kbd \ br.noacc.kbd \ by.kbd \ ca.kbd \ ca-fr.kbd \ centraleuropean.kbd \ centraleuropean.qwerty.kbd \ ch-fr.acc.kbd \ ch-fr.kbd \ ch.acc.kbd \ ch.kbd \ ch.macbook.acc.kbd \ colemak.acc.kbd \ cz.kbd \ de.acc.kbd \ de.noacc.kbd \ de.kbd \ dk.acc.kbd \ dk.kbd \ dk.macbook.kbd \ ee.kbd \ es.acc.kbd \ es.dvorak.kbd \ es.kbd \ fi.kbd \ fr.acc.kbd \ fr.dvorak.acc.kbd \ fr.dvorak.kbd \ fr.kbd \ fr.macbook.kbd \ gr.101.acc.kbd \ gr.elot.acc.kbd \ gr.kbd \ hr.kbd \ hu.101.kbd \ hu.102.kbd \ il.kbd \ is.acc.kbd \ is.kbd \ it.kbd \ jp.capsctrl.kbd \ jp.kbd \ kz.io.kbd \ kz.kst.kbd \ latinamerican.acc.kbd \ latinamerican.kbd \ lt.kbd \ nl.kbd \ no.dvorak.kbd \ no.kbd \ nordic.asus-eee.kbd \ pl.dvorak.kbd \ pl.kbd \ pt.acc.kbd \ pt.kbd \ ru.kbd \ ru.shift.kbd \ ru.win.kbd \ se.kbd \ si.kbd \ sk.kbd \ tr.f.kbd \ tr.kbd \ ua.kbd \ ua.shift.alt.kbd \ uk.capsctrl.kbd \ uk.dvorak.kbd \ uk.kbd \ + uk.macbook.kbd \ us.acc.kbd \ us.ctrl.kbd \ us.dvorak.kbd \ us.dvorakl.kbd \ us.dvorakp.kbd \ us.dvorakr.kbd \ us.dvorakx.kbd \ us.emacs.kbd \ us.kbd \ us.macbook.kbd \ us.unix.kbd \ FILESDIR= ${SHAREDIR}/vt/keymaps .include Index: projects/clang700-import/share/vt/keymaps/uk.macbook.kbd =================================================================== --- projects/clang700-import/share/vt/keymaps/uk.macbook.kbd (nonexistent) +++ projects/clang700-import/share/vt/keymaps/uk.macbook.kbd (revision 341813) @@ -0,0 +1,115 @@ +# $FreeBSD$ +# by James Wright +# alt +# scan cntrl alt alt cntrl lock +# code base shift cntrl shift alt shift cntrl shift state +# ------------------------------------------------------------------ + 000 nop nop nop nop nop nop nop nop O + 001 esc esc esc esc esc esc debug esc O + 002 '1' '!' nop nop '1' '!' nop nop O + 003 '2' '@' nul nul 0x20ac '@' nul nul O + 004 '3' 0xa3 nop nop '#' 0xa3 nop nop O + 005 '4' '$' nop nop '4' '$' nop nop O + 006 '5' '%' nop nop '5' '%' nop nop O + 007 '6' '^' rs rs '6' '^' rs rs O + 008 '7' '&' nop nop '7' '&' nop nop O + 009 '8' '*' nop nop '8' '*' nop nop O + 010 '9' '(' nop nop '9' '(' nop nop O + 011 '0' ')' nop nop '0' ')' nop nop O + 012 '-' '_' us us '-' '_' us us O + 013 '=' '+' nop nop '=' '+' nop nop O + 014 bs bs del del bs bs del del O + 015 ht btab nop nop ht btab nop nop O + 016 'q' 'Q' dc1 dc1 'q' 'Q' dc1 dc1 C + 017 'w' 'W' etb etb 'w' 'W' etb etb C + 018 'e' 'E' enq enq 'e' 'E' enq enq C + 019 'r' 'R' dc2 dc2 'r' 'R' dc2 dc2 C + 020 't' 'T' dc4 dc4 't' 'T' dc4 dc4 C + 021 'y' 'Y' em em 'y' 'Y' em em C + 022 'u' 'U' nak nak 'u' 'U' nak nak C + 023 'i' 'I' ht ht 'i' 'I' ht ht C + 024 'o' 'O' si si 'o' 'O' si si C + 025 'p' 'P' dle dle 'p' 'P' dle dle C + 026 '[' '{' esc esc '[' '{' esc esc O + 027 ']' '}' gs gs ']' '}' gs gs O + 028 cr cr nl nl cr cr nl nl O + 029 lctrl lctrl lctrl lctrl lctrl lctrl lctrl lctrl O + 030 'a' 'A' soh soh 'a' 'A' soh soh C + 031 's' 'S' dc3 dc3 's' 'S' dc3 dc3 C + 032 'd' 'D' eot eot 'd' 'D' eot eot C + 033 'f' 'F' ack ack 'f' 'F' ack ack C + 034 'g' 'G' bel bel 'g' 'G' bel bel C + 035 'h' 'H' bs bs 'h' 'H' bs bs C + 036 'j' 'J' nl nl 'j' 'J' nl nl C + 037 'k' 'K' vt vt 'k' 'K' vt vt C + 038 'l' 'L' ff ff 'l' 'L' ff ff C + 039 ';' ':' nop nop ';' ':' nop nop O + 040 ''' '"' nop nop ''' '"' nop nop O + 041 0xa7 0xb1 nop nop 0xa7 0xb1 nop nop O + 042 lshift lshift lshift lshift lshift lshift lshift lshift O + 043 '\' '|' fs fs '\' '|' fs fs O + 044 'z' 'Z' sub sub 'z' 'Z' sub sub C + 045 'x' 'X' can can 'x' 'X' can can C + 046 'c' 'C' etx etx 'c' 'C' etx etx C + 047 'v' 'V' syn syn 'v' 'V' syn syn C + 048 'b' 'B' stx stx 'b' 'B' stx stx C + 049 'n' 'N' so so 'n' 'N' so so C + 050 'm' 'M' cr cr 'm' 'M' cr cr C + 051 ',' '<' nop nop ',' '<' nop nop O + 052 '.' '>' nop nop '.' '>' nop nop O + 053 '/' '?' nop nop '/' '?' nop nop O + 054 rshift rshift rshift rshift rshift rshift rshift rshift O + 055 '*' '*' '*' '*' '*' '*' '*' '*' O + 056 lalt lalt lalt lalt lalt lalt lalt lalt O + 057 ' ' ' ' nul ' ' ' ' ' ' susp ' ' O + 058 clock clock clock clock clock clock clock clock O + 059 fkey01 fkey13 fkey25 fkey37 scr01 scr11 scr01 scr11 O + 060 fkey02 fkey14 fkey26 fkey38 scr02 scr12 scr02 scr12 O + 061 fkey03 fkey15 fkey27 fkey39 scr03 scr13 scr03 scr13 O + 062 fkey04 fkey16 fkey28 fkey40 scr04 scr14 scr04 scr14 O + 063 fkey05 fkey17 fkey29 fkey41 scr05 scr15 scr05 scr15 O + 064 fkey06 fkey18 fkey30 fkey42 scr06 scr16 scr06 scr16 O + 065 fkey07 fkey19 fkey31 fkey43 scr07 scr07 scr07 scr07 O + 066 fkey08 fkey20 fkey32 fkey44 scr08 scr08 scr08 scr08 O + 067 fkey09 fkey21 fkey33 fkey45 scr09 scr09 scr09 scr09 O + 068 fkey10 fkey22 fkey34 fkey46 scr10 scr10 scr10 scr10 O + 069 nlock nlock nlock nlock nlock nlock nlock nlock O + 070 slock slock slock slock slock slock slock slock O + 071 fkey49 '7' '7' '7' '7' '7' '7' '7' N + 072 fkey50 '8' '8' '8' '8' '8' '8' '8' N + 073 fkey51 '9' '9' '9' '9' '9' '9' '9' N + 074 fkey52 '-' '-' '-' '-' '-' '-' '-' N + 075 fkey53 '4' '4' '4' '4' '4' '4' '4' N + 076 fkey54 '5' '5' '5' '5' '5' '5' '5' N + 077 fkey55 '6' '6' '6' '6' '6' '6' '6' N + 078 fkey56 '+' '+' '+' '+' '+' '+' '+' N + 079 fkey57 '1' '1' '1' '1' '1' '1' '1' N + 080 fkey58 '2' '2' '2' '2' '2' '2' '2' N + 081 fkey59 '3' '3' '3' '3' '3' '3' '3' N + 082 fkey60 '0' '0' '0' '0' '0' '0' '0' N + 083 del '.' '.' '.' '.' '.' boot boot N + 084 nop nop nop nop nop nop nop nop O + 085 nop nop nop nop nop nop nop nop O + 086 '`' '~' nop nop '`' '~' nop nop O + 087 fkey11 fkey23 fkey35 fkey47 scr11 scr11 scr11 scr11 O + 088 fkey12 fkey24 fkey36 fkey48 scr12 scr12 scr12 scr12 O + 089 cr cr nl nl cr cr nl nl O + 090 rctrl rctrl rctrl rctrl rctrl rctrl rctrl rctrl O + 091 '/' '/' '/' '/' '/' '/' '/' '/' N + 092 nscr pscr debug debug nop nop nop nop O + 093 ralt ralt ralt ralt ralt ralt ralt ralt O + 094 fkey49 fkey49 fkey49 fkey49 fkey49 fkey49 fkey49 fkey49 O + 095 fkey50 fkey50 fkey50 fkey50 fkey50 fkey50 fkey50 fkey50 O + 096 fkey51 fkey51 fkey51 fkey51 fkey51 fkey51 fkey51 fkey51 O + 097 fkey53 fkey53 fkey53 fkey53 fkey53 fkey53 fkey53 fkey53 O + 098 fkey55 fkey55 fkey55 fkey55 fkey55 fkey55 fkey55 fkey55 O + 099 fkey57 fkey57 fkey57 fkey57 fkey57 fkey57 fkey57 fkey57 O + 100 fkey58 fkey58 fkey58 fkey58 fkey58 fkey58 fkey58 fkey58 O + 101 fkey59 fkey59 fkey59 fkey59 fkey59 fkey59 fkey59 fkey59 O + 102 fkey60 paste fkey60 fkey60 fkey60 fkey60 fkey60 fkey60 O + 103 fkey61 fkey61 fkey61 fkey61 fkey61 fkey61 boot fkey61 O + 104 slock saver slock saver susp nop susp nop O + 105 fkey62 fkey62 fkey62 fkey62 fkey62 fkey62 fkey62 fkey62 O + 106 fkey63 fkey63 fkey63 fkey63 fkey63 fkey63 fkey63 fkey63 O + 107 fkey64 fkey64 fkey64 fkey64 fkey64 fkey64 fkey64 fkey64 O + 108 nop nop nop nop nop nop nop nop O Property changes on: projects/clang700-import/share/vt/keymaps/uk.macbook.kbd ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang700-import/stand/powerpc/uboot/Makefile =================================================================== --- projects/clang700-import/stand/powerpc/uboot/Makefile (revision 341812) +++ projects/clang700-import/stand/powerpc/uboot/Makefile (revision 341813) @@ -1,33 +1,33 @@ # $FreeBSD$ LOADER_UFS_SUPPORT?= yes LOADER_CD9660_SUPPORT?= no LOADER_EXT2FS_SUPPORT?= no LOADER_NET_SUPPORT?= yes LOADER_NFS_SUPPORT?= yes LOADER_TFTP_SUPPORT?= no LOADER_GZIP_SUPPORT?= no LOADER_BZIP2_SUPPORT?= no .include PROG= ubldr NEWVERSWHAT= "U-Boot loader" ${MACHINE_ARCH} INSTALLFLAGS= -b # Architecture-specific loader code -SRCS= start.S conf.c vers.c +SRCS= start.S conf.c vers.c ppc64_elf_freebsd.c SRCS+= ucmpdi2.c # Always add MI sources .include "${BOOTSRC}/loader.mk" .PATH: ${SYSDIR}/libkern LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc .include "${BOOTSRC}/uboot.mk" DPADD= ${LDR_INTERP} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSA} LDADD= ${LDR_INTERP} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSA} .include Index: projects/clang700-import/stand/powerpc/uboot/conf.c =================================================================== --- projects/clang700-import/stand/powerpc/uboot/conf.c (revision 341812) +++ projects/clang700-import/stand/powerpc/uboot/conf.c (revision 341813) @@ -1,112 +1,114 @@ /*- * Copyright (c) 1999 Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include "bootstrap.h" #include "libuboot.h" #if defined(LOADER_NET_SUPPORT) #include "dev_net.h" #endif /* Make sure we have an explicit reference to exit so libsa's panic pulls in the MD exit */ void (*exitfn)(int) = exit; /* * We could use linker sets for some or all of these, but * then we would have to control what ended up linked into * the bootstrap. So it's easier to conditionalise things * here. * * XXX rename these arrays to be consistent and less namespace-hostile */ /* Exported for libstand */ struct devsw *devsw[] = { #if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT) &uboot_storage, #endif #if defined(LOADER_NET_SUPPORT) &netdev, #endif NULL }; struct fs_ops *file_system[] = { #if defined(LOADER_UFS_SUPPORT) &ufs_fsops, #endif #if defined(LOADER_CD9660_SUPPORT) &cd9660_fsops, #endif #if defined(LOADER_EXT2FS_SUPPORT) &ext2fs_fsops, #endif #if defined(LOADER_NFS_SUPPORT) &nfs_fsops, #endif #if defined(LOADER_TFTP_SUPPORT) &tftp_fsops, #endif #if defined(LOADER_GZIP_SUPPORT) &gzipfs_fsops, #endif #if defined(LOADER_BZIP2_SUPPORT) &bzipfs_fsops, #endif NULL }; struct netif_driver *netif_drivers[] = { #if defined(LOADER_NET_SUPPORT) &uboot_net, #endif NULL, }; /* Exported for PowerPC only */ /* * Sort formats so that those that can detect based on arguments * rather than reading the file go first. */ +extern struct file_format uboot_elf64; struct file_format *file_formats[] = { &uboot_elf, + &uboot_elf64, NULL }; /* * Consoles */ extern struct console uboot_console; struct console *consoles[] = { &uboot_console, NULL }; Index: projects/clang700-import/stand/powerpc/uboot/ppc64_elf_freebsd.c =================================================================== --- projects/clang700-import/stand/powerpc/uboot/ppc64_elf_freebsd.c (nonexistent) +++ projects/clang700-import/stand/powerpc/uboot/ppc64_elf_freebsd.c (revision 341813) @@ -0,0 +1,101 @@ +/*- + * Copyright (c) 2001 Benno Rice + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 64 + +#include +#include + +#include +#include +#include + +#include + +#include "bootstrap.h" +#include "libuboot.h" + +vm_offset_t md_load64(char *args, vm_offset_t *modulep, vm_offset_t *dtb); +extern char end[]; +extern vm_offset_t reloc; /* From /conf.c */ + +int +ppc64_uboot_elf_loadfile(char *filename, uint64_t dest, + struct preloaded_file **result) +{ + int r; + + r = __elfN(loadfile)(filename, dest, result); + if (r != 0) + return (r); + + /* + * No need to sync the icache for modules: this will + * be done by the kernel after relocation. + */ + if (!strcmp((*result)->f_type, "elf kernel")) + __syncicache((void *) (*result)->f_addr, (*result)->f_size); + return (0); +} + +int +ppc64_uboot_elf_exec(struct preloaded_file *fp) +{ + struct file_metadata *fmp; + vm_offset_t mdp, dtbp; + Elf_Ehdr *e; + int error; + void (*entry)(void *); + + if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { + return(EFTYPE); + } + e = (Elf_Ehdr *)&fmp->md_data; + + /* Handle function descriptor for ELFv1 kernels */ + if ((e->e_flags & 3) == 2) + entry = (void (*)(void*))(intptr_t)e->e_entry; + else + entry = *(void (*)(void*))(uint64_t *)(intptr_t)e->e_entry; + + if ((error = md_load64(fp->f_args, &mdp, &dtbp)) != 0) + return (error); + + dev_cleanup(); + printf("Kernel args: %s\n", fp->f_args); + + (*entry)((void *)mdp); + panic("exec returned"); +} + +struct file_format uboot_elf64 = +{ + ppc64_uboot_elf_loadfile, + ppc64_uboot_elf_exec +}; Property changes on: projects/clang700-import/stand/powerpc/uboot/ppc64_elf_freebsd.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang700-import/sys/arm/include/atomic.h =================================================================== --- projects/clang700-import/sys/arm/include/atomic.h (revision 341812) +++ projects/clang700-import/sys/arm/include/atomic.h (revision 341813) @@ -1,99 +1,106 @@ /* $NetBSD: atomic.h,v 1.1 2002/10/19 12:22:34 bsh Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 2003-2004 Olivier Houchard * Copyright (C) 1994-1997 Mark Brinicombe * Copyright (C) 1994 Brini * All rights reserved. * * This code is derived from software written for Brini by Mark Brinicombe * * 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 Brini. * 4. The name of Brini may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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 _MACHINE_ATOMIC_H_ #define _MACHINE_ATOMIC_H_ #include #include #ifndef _KERNEL #include #endif #if __ARM_ARCH >= 6 #include #else /* < armv6 */ #include #endif /* Arch >= v6 */ +static __inline u_long +atomic_swap_long(volatile u_long *p, u_long v) +{ + + return (atomic_swap_32((volatile uint32_t *)p, v)); +} + #define atomic_clear_ptr atomic_clear_32 #define atomic_clear_acq_ptr atomic_clear_acq_32 #define atomic_clear_rel_ptr atomic_clear_rel_32 #define atomic_set_ptr atomic_set_32 #define atomic_set_acq_ptr atomic_set_acq_32 #define atomic_set_rel_ptr atomic_set_rel_32 #define atomic_fcmpset_ptr atomic_fcmpset_32 #define atomic_fcmpset_rel_ptr atomic_fcmpset_rel_32 #define atomic_fcmpset_acq_ptr atomic_fcmpset_acq_32 #define atomic_cmpset_ptr atomic_cmpset_32 #define atomic_cmpset_acq_ptr atomic_cmpset_acq_32 #define atomic_cmpset_rel_ptr atomic_cmpset_rel_32 #define atomic_load_acq_ptr atomic_load_acq_32 #define atomic_store_rel_ptr atomic_store_rel_32 #define atomic_swap_ptr atomic_swap_32 #define atomic_readandclear_ptr atomic_readandclear_32 #define atomic_add_int atomic_add_32 #define atomic_add_acq_int atomic_add_acq_32 #define atomic_add_rel_int atomic_add_rel_32 #define atomic_subtract_int atomic_subtract_32 #define atomic_subtract_acq_int atomic_subtract_acq_32 #define atomic_subtract_rel_int atomic_subtract_rel_32 #define atomic_clear_int atomic_clear_32 #define atomic_clear_acq_int atomic_clear_acq_32 #define atomic_clear_rel_int atomic_clear_rel_32 #define atomic_set_int atomic_set_32 #define atomic_set_acq_int atomic_set_acq_32 #define atomic_set_rel_int atomic_set_rel_32 #define atomic_fcmpset_int atomic_fcmpset_32 #define atomic_fcmpset_acq_int atomic_fcmpset_acq_32 #define atomic_fcmpset_rel_int atomic_fcmpset_rel_32 #define atomic_cmpset_int atomic_cmpset_32 #define atomic_cmpset_acq_int atomic_cmpset_acq_32 #define atomic_cmpset_rel_int atomic_cmpset_rel_32 #define atomic_fetchadd_int atomic_fetchadd_32 #define atomic_readandclear_int atomic_readandclear_32 #define atomic_load_acq_int atomic_load_acq_32 #define atomic_store_rel_int atomic_store_rel_32 #define atomic_swap_int atomic_swap_32 #endif /* _MACHINE_ATOMIC_H_ */ Index: projects/clang700-import/sys/cam/scsi/scsi_all.c =================================================================== --- projects/clang700-import/sys/cam/scsi/scsi_all.c (revision 341812) +++ projects/clang700-import/sys/cam/scsi/scsi_all.c (revision 341813) @@ -1,9265 +1,9265 @@ /*- * Implementation of Utility functions for all SCSI device types. * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs. * Copyright (c) 1997, 1998, 2003 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 #ifdef _KERNEL #include "opt_scsi.h" #include #include #include #include #include #include #include #include #else #include #include #include #include #include #endif #include #include #include #include #include #include #include #ifdef _KERNEL #include #include #include #include #else #include #include #ifndef FALSE #define FALSE 0 #endif /* FALSE */ #ifndef TRUE #define TRUE 1 #endif /* TRUE */ #define ERESTART -1 /* restart syscall */ #define EJUSTRETURN -2 /* don't modify regs, just return */ #endif /* !_KERNEL */ /* * This is the default number of milliseconds we wait for devices to settle * after a SCSI bus reset. */ #ifndef SCSI_DELAY #define SCSI_DELAY 2000 #endif /* * All devices need _some_ sort of bus settle delay, so we'll set it to * a minimum value of 100ms. Note that this is pertinent only for SPI- * not transport like Fibre Channel or iSCSI where 'delay' is completely * meaningless. */ #ifndef SCSI_MIN_DELAY #define SCSI_MIN_DELAY 100 #endif /* * Make sure the user isn't using seconds instead of milliseconds. */ #if (SCSI_DELAY < SCSI_MIN_DELAY && SCSI_DELAY != 0) #error "SCSI_DELAY is in milliseconds, not seconds! Please use a larger value" #endif int scsi_delay; static int ascentrycomp(const void *key, const void *member); static int senseentrycomp(const void *key, const void *member); static void fetchtableentries(int sense_key, int asc, int ascq, struct scsi_inquiry_data *, const struct sense_key_table_entry **, const struct asc_table_entry **); #ifdef _KERNEL static void init_scsi_delay(void); static int sysctl_scsi_delay(SYSCTL_HANDLER_ARGS); static int set_scsi_delay(int delay); #endif #if !defined(SCSI_NO_OP_STRINGS) #define D (1 << T_DIRECT) #define T (1 << T_SEQUENTIAL) #define L (1 << T_PRINTER) #define P (1 << T_PROCESSOR) #define W (1 << T_WORM) #define R (1 << T_CDROM) #define O (1 << T_OPTICAL) #define M (1 << T_CHANGER) #define A (1 << T_STORARRAY) #define E (1 << T_ENCLOSURE) #define B (1 << T_RBC) #define K (1 << T_OCRW) #define V (1 << T_ADC) #define F (1 << T_OSD) #define S (1 << T_SCANNER) #define C (1 << T_COMM) #define ALL (D | T | L | P | W | R | O | M | A | E | B | K | V | F | S | C) static struct op_table_entry plextor_cd_ops[] = { { 0xD8, R, "CD-DA READ" } }; static struct scsi_op_quirk_entry scsi_op_quirk_table[] = { { /* * I believe that 0xD8 is the Plextor proprietary command * to read CD-DA data. I'm not sure which Plextor CDROM * models support the command, though. I know for sure * that the 4X, 8X, and 12X models do, and presumably the * 12-20X does. I don't know about any earlier models, * though. If anyone has any more complete information, * feel free to change this quirk entry. */ {T_CDROM, SIP_MEDIA_REMOVABLE, "PLEXTOR", "CD-ROM PX*", "*"}, nitems(plextor_cd_ops), plextor_cd_ops } }; static struct op_table_entry scsi_op_codes[] = { /* * From: http://www.t10.org/lists/op-num.txt * Modifications by Kenneth Merry (ken@FreeBSD.ORG) * and Jung-uk Kim (jkim@FreeBSD.org) * * Note: order is important in this table, scsi_op_desc() currently * depends on the opcodes in the table being in order to save * search time. * Note: scanner and comm. devices are carried over from the previous * version because they were removed in the latest spec. */ /* File: OP-NUM.TXT * * SCSI Operation Codes * Numeric Sorted Listing * as of 5/26/15 * * D - DIRECT ACCESS DEVICE (SBC-2) device column key * .T - SEQUENTIAL ACCESS DEVICE (SSC-2) ----------------- * . L - PRINTER DEVICE (SSC) M = Mandatory * . P - PROCESSOR DEVICE (SPC) O = Optional * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC-2) V = Vendor spec. * . . R - CD/DVE DEVICE (MMC-3) Z = Obsolete * . . O - OPTICAL MEMORY DEVICE (SBC-2) * . . .M - MEDIA CHANGER DEVICE (SMC-2) * . . . A - STORAGE ARRAY DEVICE (SCC-2) * . . . .E - ENCLOSURE SERVICES DEVICE (SES) * . . . .B - SIMPLIFIED DIRECT-ACCESS DEVICE (RBC) * . . . . K - OPTICAL CARD READER/WRITER DEVICE (OCRW) * . . . . V - AUTOMATION/DRIVE INTERFACE (ADC) * . . . . .F - OBJECT-BASED STORAGE (OSD) * OP DTLPWROMAEBKVF Description * -- -------------- ---------------------------------------------- */ /* 00 MMMMMMMMMMMMMM TEST UNIT READY */ { 0x00, ALL, "TEST UNIT READY" }, /* 01 M REWIND */ { 0x01, T, "REWIND" }, /* 01 Z V ZZZZ REZERO UNIT */ { 0x01, D | W | R | O | M, "REZERO UNIT" }, /* 02 VVVVVV V */ /* 03 MMMMMMMMMMOMMM REQUEST SENSE */ { 0x03, ALL, "REQUEST SENSE" }, /* 04 M OO FORMAT UNIT */ { 0x04, D | R | O, "FORMAT UNIT" }, /* 04 O FORMAT MEDIUM */ { 0x04, T, "FORMAT MEDIUM" }, /* 04 O FORMAT */ { 0x04, L, "FORMAT" }, /* 05 VMVVVV V READ BLOCK LIMITS */ { 0x05, T, "READ BLOCK LIMITS" }, /* 06 VVVVVV V */ /* 07 OVV O OV REASSIGN BLOCKS */ { 0x07, D | W | O, "REASSIGN BLOCKS" }, /* 07 O INITIALIZE ELEMENT STATUS */ { 0x07, M, "INITIALIZE ELEMENT STATUS" }, /* 08 MOV O OV READ(6) */ { 0x08, D | T | W | O, "READ(6)" }, /* 08 O RECEIVE */ { 0x08, P, "RECEIVE" }, /* 08 GET MESSAGE(6) */ { 0x08, C, "GET MESSAGE(6)" }, /* 09 VVVVVV V */ /* 0A OO O OV WRITE(6) */ { 0x0A, D | T | W | O, "WRITE(6)" }, /* 0A M SEND(6) */ { 0x0A, P, "SEND(6)" }, /* 0A SEND MESSAGE(6) */ { 0x0A, C, "SEND MESSAGE(6)" }, /* 0A M PRINT */ { 0x0A, L, "PRINT" }, /* 0B Z ZOZV SEEK(6) */ { 0x0B, D | W | R | O, "SEEK(6)" }, /* 0B O SET CAPACITY */ { 0x0B, T, "SET CAPACITY" }, /* 0B O SLEW AND PRINT */ { 0x0B, L, "SLEW AND PRINT" }, /* 0C VVVVVV V */ /* 0D VVVVVV V */ /* 0E VVVVVV V */ /* 0F VOVVVV V READ REVERSE(6) */ { 0x0F, T, "READ REVERSE(6)" }, /* 10 VM VVV WRITE FILEMARKS(6) */ { 0x10, T, "WRITE FILEMARKS(6)" }, /* 10 O SYNCHRONIZE BUFFER */ { 0x10, L, "SYNCHRONIZE BUFFER" }, /* 11 VMVVVV SPACE(6) */ { 0x11, T, "SPACE(6)" }, /* 12 MMMMMMMMMMMMMM INQUIRY */ { 0x12, ALL, "INQUIRY" }, /* 13 V VVVV */ /* 13 O VERIFY(6) */ { 0x13, T, "VERIFY(6)" }, /* 14 VOOVVV RECOVER BUFFERED DATA */ { 0x14, T | L, "RECOVER BUFFERED DATA" }, /* 15 OMO O OOOO OO MODE SELECT(6) */ { 0x15, ALL & ~(P | R | B | F), "MODE SELECT(6)" }, /* 16 ZZMZO OOOZ O RESERVE(6) */ { 0x16, ALL & ~(R | B | V | F | C), "RESERVE(6)" }, /* 16 Z RESERVE ELEMENT(6) */ { 0x16, M, "RESERVE ELEMENT(6)" }, /* 17 ZZMZO OOOZ O RELEASE(6) */ { 0x17, ALL & ~(R | B | V | F | C), "RELEASE(6)" }, /* 17 Z RELEASE ELEMENT(6) */ { 0x17, M, "RELEASE ELEMENT(6)" }, /* 18 ZZZZOZO Z COPY */ { 0x18, D | T | L | P | W | R | O | K | S, "COPY" }, /* 19 VMVVVV ERASE(6) */ { 0x19, T, "ERASE(6)" }, /* 1A OMO O OOOO OO MODE SENSE(6) */ { 0x1A, ALL & ~(P | R | B | F), "MODE SENSE(6)" }, /* 1B O OOO O MO O START STOP UNIT */ { 0x1B, D | W | R | O | A | B | K | F, "START STOP UNIT" }, /* 1B O M LOAD UNLOAD */ { 0x1B, T | V, "LOAD UNLOAD" }, /* 1B SCAN */ { 0x1B, S, "SCAN" }, /* 1B O STOP PRINT */ { 0x1B, L, "STOP PRINT" }, /* 1B O OPEN/CLOSE IMPORT/EXPORT ELEMENT */ { 0x1B, M, "OPEN/CLOSE IMPORT/EXPORT ELEMENT" }, /* 1C OOOOO OOOM OOO RECEIVE DIAGNOSTIC RESULTS */ { 0x1C, ALL & ~(R | B), "RECEIVE DIAGNOSTIC RESULTS" }, /* 1D MMMMM MMOM MMM SEND DIAGNOSTIC */ { 0x1D, ALL & ~(R | B), "SEND DIAGNOSTIC" }, /* 1E OO OOOO O O PREVENT ALLOW MEDIUM REMOVAL */ { 0x1E, D | T | W | R | O | M | K | F, "PREVENT ALLOW MEDIUM REMOVAL" }, /* 1F */ /* 20 V VVV V */ /* 21 V VVV V */ /* 22 V VVV V */ /* 23 V V V V */ /* 23 O READ FORMAT CAPACITIES */ { 0x23, R, "READ FORMAT CAPACITIES" }, /* 24 V VV SET WINDOW */ { 0x24, S, "SET WINDOW" }, /* 25 M M M M READ CAPACITY(10) */ { 0x25, D | W | O | B, "READ CAPACITY(10)" }, /* 25 O READ CAPACITY */ { 0x25, R, "READ CAPACITY" }, /* 25 M READ CARD CAPACITY */ { 0x25, K, "READ CARD CAPACITY" }, /* 25 GET WINDOW */ { 0x25, S, "GET WINDOW" }, /* 26 V VV */ /* 27 V VV */ /* 28 M MOM MM READ(10) */ { 0x28, D | W | R | O | B | K | S, "READ(10)" }, /* 28 GET MESSAGE(10) */ { 0x28, C, "GET MESSAGE(10)" }, /* 29 V VVO READ GENERATION */ { 0x29, O, "READ GENERATION" }, /* 2A O MOM MO WRITE(10) */ { 0x2A, D | W | R | O | B | K, "WRITE(10)" }, /* 2A SEND(10) */ { 0x2A, S, "SEND(10)" }, /* 2A SEND MESSAGE(10) */ { 0x2A, C, "SEND MESSAGE(10)" }, /* 2B Z OOO O SEEK(10) */ { 0x2B, D | W | R | O | K, "SEEK(10)" }, /* 2B O LOCATE(10) */ { 0x2B, T, "LOCATE(10)" }, /* 2B O POSITION TO ELEMENT */ { 0x2B, M, "POSITION TO ELEMENT" }, /* 2C V OO ERASE(10) */ { 0x2C, R | O, "ERASE(10)" }, /* 2D O READ UPDATED BLOCK */ { 0x2D, O, "READ UPDATED BLOCK" }, /* 2D V */ /* 2E O OOO MO WRITE AND VERIFY(10) */ { 0x2E, D | W | R | O | B | K, "WRITE AND VERIFY(10)" }, /* 2F O OOO VERIFY(10) */ { 0x2F, D | W | R | O, "VERIFY(10)" }, /* 30 Z ZZZ SEARCH DATA HIGH(10) */ { 0x30, D | W | R | O, "SEARCH DATA HIGH(10)" }, /* 31 Z ZZZ SEARCH DATA EQUAL(10) */ { 0x31, D | W | R | O, "SEARCH DATA EQUAL(10)" }, /* 31 OBJECT POSITION */ { 0x31, S, "OBJECT POSITION" }, /* 32 Z ZZZ SEARCH DATA LOW(10) */ { 0x32, D | W | R | O, "SEARCH DATA LOW(10)" }, /* 33 Z OZO SET LIMITS(10) */ { 0x33, D | W | R | O, "SET LIMITS(10)" }, /* 34 O O O O PRE-FETCH(10) */ { 0x34, D | W | O | K, "PRE-FETCH(10)" }, /* 34 M READ POSITION */ { 0x34, T, "READ POSITION" }, /* 34 GET DATA BUFFER STATUS */ { 0x34, S, "GET DATA BUFFER STATUS" }, /* 35 O OOO MO SYNCHRONIZE CACHE(10) */ { 0x35, D | W | R | O | B | K, "SYNCHRONIZE CACHE(10)" }, /* 36 Z O O O LOCK UNLOCK CACHE(10) */ { 0x36, D | W | O | K, "LOCK UNLOCK CACHE(10)" }, /* 37 O O READ DEFECT DATA(10) */ { 0x37, D | O, "READ DEFECT DATA(10)" }, /* 37 O INITIALIZE ELEMENT STATUS WITH RANGE */ { 0x37, M, "INITIALIZE ELEMENT STATUS WITH RANGE" }, /* 38 O O O MEDIUM SCAN */ { 0x38, W | O | K, "MEDIUM SCAN" }, /* 39 ZZZZOZO Z COMPARE */ { 0x39, D | T | L | P | W | R | O | K | S, "COMPARE" }, /* 3A ZZZZOZO Z COPY AND VERIFY */ { 0x3A, D | T | L | P | W | R | O | K | S, "COPY AND VERIFY" }, /* 3B OOOOOOOOOOMOOO WRITE BUFFER */ { 0x3B, ALL, "WRITE BUFFER" }, /* 3C OOOOOOOOOO OOO READ BUFFER */ { 0x3C, ALL & ~(B), "READ BUFFER" }, /* 3D O UPDATE BLOCK */ { 0x3D, O, "UPDATE BLOCK" }, /* 3E O O O READ LONG(10) */ { 0x3E, D | W | O, "READ LONG(10)" }, /* 3F O O O WRITE LONG(10) */ { 0x3F, D | W | O, "WRITE LONG(10)" }, /* 40 ZZZZOZOZ CHANGE DEFINITION */ { 0x40, D | T | L | P | W | R | O | M | S | C, "CHANGE DEFINITION" }, /* 41 O WRITE SAME(10) */ { 0x41, D, "WRITE SAME(10)" }, /* 42 O UNMAP */ { 0x42, D, "UNMAP" }, /* 42 O READ SUB-CHANNEL */ { 0x42, R, "READ SUB-CHANNEL" }, /* 43 O READ TOC/PMA/ATIP */ { 0x43, R, "READ TOC/PMA/ATIP" }, /* 44 M M REPORT DENSITY SUPPORT */ { 0x44, T | V, "REPORT DENSITY SUPPORT" }, /* 44 READ HEADER */ /* 45 O PLAY AUDIO(10) */ { 0x45, R, "PLAY AUDIO(10)" }, /* 46 M GET CONFIGURATION */ { 0x46, R, "GET CONFIGURATION" }, /* 47 O PLAY AUDIO MSF */ { 0x47, R, "PLAY AUDIO MSF" }, /* 48 */ /* 49 */ /* 4A M GET EVENT STATUS NOTIFICATION */ { 0x4A, R, "GET EVENT STATUS NOTIFICATION" }, /* 4B O PAUSE/RESUME */ { 0x4B, R, "PAUSE/RESUME" }, /* 4C OOOOO OOOO OOO LOG SELECT */ { 0x4C, ALL & ~(R | B), "LOG SELECT" }, /* 4D OOOOO OOOO OMO LOG SENSE */ { 0x4D, ALL & ~(R | B), "LOG SENSE" }, /* 4E O STOP PLAY/SCAN */ { 0x4E, R, "STOP PLAY/SCAN" }, /* 4F */ /* 50 O XDWRITE(10) */ { 0x50, D, "XDWRITE(10)" }, /* 51 O XPWRITE(10) */ { 0x51, D, "XPWRITE(10)" }, /* 51 O READ DISC INFORMATION */ { 0x51, R, "READ DISC INFORMATION" }, /* 52 O XDREAD(10) */ { 0x52, D, "XDREAD(10)" }, /* 52 O READ TRACK INFORMATION */ { 0x52, R, "READ TRACK INFORMATION" }, /* 53 O RESERVE TRACK */ { 0x53, R, "RESERVE TRACK" }, /* 54 O SEND OPC INFORMATION */ { 0x54, R, "SEND OPC INFORMATION" }, /* 55 OOO OMOOOOMOMO MODE SELECT(10) */ { 0x55, ALL & ~(P), "MODE SELECT(10)" }, /* 56 ZZMZO OOOZ RESERVE(10) */ { 0x56, ALL & ~(R | B | K | V | F | C), "RESERVE(10)" }, /* 56 Z RESERVE ELEMENT(10) */ { 0x56, M, "RESERVE ELEMENT(10)" }, /* 57 ZZMZO OOOZ RELEASE(10) */ { 0x57, ALL & ~(R | B | K | V | F | C), "RELEASE(10)" }, /* 57 Z RELEASE ELEMENT(10) */ { 0x57, M, "RELEASE ELEMENT(10)" }, /* 58 O REPAIR TRACK */ { 0x58, R, "REPAIR TRACK" }, /* 59 */ /* 5A OOO OMOOOOMOMO MODE SENSE(10) */ { 0x5A, ALL & ~(P), "MODE SENSE(10)" }, /* 5B O CLOSE TRACK/SESSION */ { 0x5B, R, "CLOSE TRACK/SESSION" }, /* 5C O READ BUFFER CAPACITY */ { 0x5C, R, "READ BUFFER CAPACITY" }, /* 5D O SEND CUE SHEET */ { 0x5D, R, "SEND CUE SHEET" }, /* 5E OOOOO OOOO M PERSISTENT RESERVE IN */ { 0x5E, ALL & ~(R | B | K | V | C), "PERSISTENT RESERVE IN" }, /* 5F OOOOO OOOO M PERSISTENT RESERVE OUT */ { 0x5F, ALL & ~(R | B | K | V | C), "PERSISTENT RESERVE OUT" }, /* 7E OO O OOOO O extended CDB */ { 0x7E, D | T | R | M | A | E | B | V, "extended CDB" }, /* 7F O M variable length CDB (more than 16 bytes) */ { 0x7F, D | F, "variable length CDB (more than 16 bytes)" }, /* 80 Z XDWRITE EXTENDED(16) */ { 0x80, D, "XDWRITE EXTENDED(16)" }, /* 80 M WRITE FILEMARKS(16) */ { 0x80, T, "WRITE FILEMARKS(16)" }, /* 81 Z REBUILD(16) */ { 0x81, D, "REBUILD(16)" }, /* 81 O READ REVERSE(16) */ { 0x81, T, "READ REVERSE(16)" }, /* 82 Z REGENERATE(16) */ { 0x82, D, "REGENERATE(16)" }, /* 83 OOOOO O OO EXTENDED COPY */ { 0x83, D | T | L | P | W | O | K | V, "EXTENDED COPY" }, /* 84 OOOOO O OO RECEIVE COPY RESULTS */ { 0x84, D | T | L | P | W | O | K | V, "RECEIVE COPY RESULTS" }, /* 85 O O O ATA COMMAND PASS THROUGH(16) */ { 0x85, D | R | B, "ATA COMMAND PASS THROUGH(16)" }, /* 86 OO OO OOOOOOO ACCESS CONTROL IN */ { 0x86, ALL & ~(L | R | F), "ACCESS CONTROL IN" }, /* 87 OO OO OOOOOOO ACCESS CONTROL OUT */ { 0x87, ALL & ~(L | R | F), "ACCESS CONTROL OUT" }, /* 88 MM O O O READ(16) */ { 0x88, D | T | W | O | B, "READ(16)" }, /* 89 O COMPARE AND WRITE*/ { 0x89, D, "COMPARE AND WRITE" }, /* 8A OM O O O WRITE(16) */ { 0x8A, D | T | W | O | B, "WRITE(16)" }, /* 8B O ORWRITE */ { 0x8B, D, "ORWRITE" }, /* 8C OO O OO O M READ ATTRIBUTE */ { 0x8C, D | T | W | O | M | B | V, "READ ATTRIBUTE" }, /* 8D OO O OO O O WRITE ATTRIBUTE */ { 0x8D, D | T | W | O | M | B | V, "WRITE ATTRIBUTE" }, /* 8E O O O O WRITE AND VERIFY(16) */ { 0x8E, D | W | O | B, "WRITE AND VERIFY(16)" }, /* 8F OO O O O VERIFY(16) */ { 0x8F, D | T | W | O | B, "VERIFY(16)" }, /* 90 O O O O PRE-FETCH(16) */ { 0x90, D | W | O | B, "PRE-FETCH(16)" }, /* 91 O O O O SYNCHRONIZE CACHE(16) */ { 0x91, D | W | O | B, "SYNCHRONIZE CACHE(16)" }, /* 91 O SPACE(16) */ { 0x91, T, "SPACE(16)" }, /* 92 Z O O LOCK UNLOCK CACHE(16) */ { 0x92, D | W | O, "LOCK UNLOCK CACHE(16)" }, /* 92 O LOCATE(16) */ { 0x92, T, "LOCATE(16)" }, /* 93 O WRITE SAME(16) */ { 0x93, D, "WRITE SAME(16)" }, /* 93 M ERASE(16) */ { 0x93, T, "ERASE(16)" }, /* 94 O ZBC OUT */ { 0x94, ALL, "ZBC OUT" }, /* 95 O ZBC IN */ { 0x95, ALL, "ZBC IN" }, /* 96 */ /* 97 */ /* 98 */ /* 99 */ /* 9A O WRITE STREAM(16) */ { 0x9A, D, "WRITE STREAM(16)" }, /* 9B OOOOOOOOOO OOO READ BUFFER(16) */ { 0x9B, ALL & ~(B) , "READ BUFFER(16)" }, /* 9C O WRITE ATOMIC(16) */ { 0x9C, D, "WRITE ATOMIC(16)" }, /* 9D SERVICE ACTION BIDIRECTIONAL */ { 0x9D, ALL, "SERVICE ACTION BIDIRECTIONAL" }, /* XXX KDM ALL for this? op-num.txt defines it for none.. */ /* 9E SERVICE ACTION IN(16) */ { 0x9E, ALL, "SERVICE ACTION IN(16)" }, /* 9F M SERVICE ACTION OUT(16) */ { 0x9F, ALL, "SERVICE ACTION OUT(16)" }, /* A0 MMOOO OMMM OMO REPORT LUNS */ { 0xA0, ALL & ~(R | B), "REPORT LUNS" }, /* A1 O BLANK */ { 0xA1, R, "BLANK" }, /* A1 O O ATA COMMAND PASS THROUGH(12) */ { 0xA1, D | B, "ATA COMMAND PASS THROUGH(12)" }, /* A2 OO O O SECURITY PROTOCOL IN */ { 0xA2, D | T | R | V, "SECURITY PROTOCOL IN" }, /* A3 OOO O OOMOOOM MAINTENANCE (IN) */ { 0xA3, ALL & ~(P | R | F), "MAINTENANCE (IN)" }, /* A3 O SEND KEY */ { 0xA3, R, "SEND KEY" }, /* A4 OOO O OOOOOOO MAINTENANCE (OUT) */ { 0xA4, ALL & ~(P | R | F), "MAINTENANCE (OUT)" }, /* A4 O REPORT KEY */ { 0xA4, R, "REPORT KEY" }, /* A5 O O OM MOVE MEDIUM */ { 0xA5, T | W | O | M, "MOVE MEDIUM" }, /* A5 O PLAY AUDIO(12) */ { 0xA5, R, "PLAY AUDIO(12)" }, /* A6 O EXCHANGE MEDIUM */ { 0xA6, M, "EXCHANGE MEDIUM" }, /* A6 O LOAD/UNLOAD C/DVD */ { 0xA6, R, "LOAD/UNLOAD C/DVD" }, /* A7 ZZ O O MOVE MEDIUM ATTACHED */ { 0xA7, D | T | W | O, "MOVE MEDIUM ATTACHED" }, /* A7 O SET READ AHEAD */ { 0xA7, R, "SET READ AHEAD" }, /* A8 O OOO READ(12) */ { 0xA8, D | W | R | O, "READ(12)" }, /* A8 GET MESSAGE(12) */ { 0xA8, C, "GET MESSAGE(12)" }, /* A9 O SERVICE ACTION OUT(12) */ { 0xA9, V, "SERVICE ACTION OUT(12)" }, /* AA O OOO WRITE(12) */ { 0xAA, D | W | R | O, "WRITE(12)" }, /* AA SEND MESSAGE(12) */ { 0xAA, C, "SEND MESSAGE(12)" }, /* AB O O SERVICE ACTION IN(12) */ { 0xAB, R | V, "SERVICE ACTION IN(12)" }, /* AC O ERASE(12) */ { 0xAC, O, "ERASE(12)" }, /* AC O GET PERFORMANCE */ { 0xAC, R, "GET PERFORMANCE" }, /* AD O READ DVD STRUCTURE */ { 0xAD, R, "READ DVD STRUCTURE" }, /* AE O O O WRITE AND VERIFY(12) */ { 0xAE, D | W | O, "WRITE AND VERIFY(12)" }, /* AF O OZO VERIFY(12) */ { 0xAF, D | W | R | O, "VERIFY(12)" }, /* B0 ZZZ SEARCH DATA HIGH(12) */ { 0xB0, W | R | O, "SEARCH DATA HIGH(12)" }, /* B1 ZZZ SEARCH DATA EQUAL(12) */ { 0xB1, W | R | O, "SEARCH DATA EQUAL(12)" }, /* B2 ZZZ SEARCH DATA LOW(12) */ { 0xB2, W | R | O, "SEARCH DATA LOW(12)" }, /* B3 Z OZO SET LIMITS(12) */ { 0xB3, D | W | R | O, "SET LIMITS(12)" }, /* B4 ZZ OZO READ ELEMENT STATUS ATTACHED */ { 0xB4, D | T | W | R | O, "READ ELEMENT STATUS ATTACHED" }, /* B5 OO O O SECURITY PROTOCOL OUT */ { 0xB5, D | T | R | V, "SECURITY PROTOCOL OUT" }, /* B5 O REQUEST VOLUME ELEMENT ADDRESS */ { 0xB5, M, "REQUEST VOLUME ELEMENT ADDRESS" }, /* B6 O SEND VOLUME TAG */ { 0xB6, M, "SEND VOLUME TAG" }, /* B6 O SET STREAMING */ { 0xB6, R, "SET STREAMING" }, /* B7 O O READ DEFECT DATA(12) */ { 0xB7, D | O, "READ DEFECT DATA(12)" }, /* B8 O OZOM READ ELEMENT STATUS */ { 0xB8, T | W | R | O | M, "READ ELEMENT STATUS" }, /* B9 O READ CD MSF */ { 0xB9, R, "READ CD MSF" }, /* BA O O OOMO REDUNDANCY GROUP (IN) */ { 0xBA, D | W | O | M | A | E, "REDUNDANCY GROUP (IN)" }, /* BA O SCAN */ { 0xBA, R, "SCAN" }, /* BB O O OOOO REDUNDANCY GROUP (OUT) */ { 0xBB, D | W | O | M | A | E, "REDUNDANCY GROUP (OUT)" }, /* BB O SET CD SPEED */ { 0xBB, R, "SET CD SPEED" }, /* BC O O OOMO SPARE (IN) */ { 0xBC, D | W | O | M | A | E, "SPARE (IN)" }, /* BD O O OOOO SPARE (OUT) */ { 0xBD, D | W | O | M | A | E, "SPARE (OUT)" }, /* BD O MECHANISM STATUS */ { 0xBD, R, "MECHANISM STATUS" }, /* BE O O OOMO VOLUME SET (IN) */ { 0xBE, D | W | O | M | A | E, "VOLUME SET (IN)" }, /* BE O READ CD */ { 0xBE, R, "READ CD" }, /* BF O O OOOO VOLUME SET (OUT) */ { 0xBF, D | W | O | M | A | E, "VOLUME SET (OUT)" }, /* BF O SEND DVD STRUCTURE */ { 0xBF, R, "SEND DVD STRUCTURE" } }; const char * scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data) { caddr_t match; int i, j; u_int32_t opmask; u_int16_t pd_type; int num_ops[2]; struct op_table_entry *table[2]; int num_tables; /* * If we've got inquiry data, use it to determine what type of * device we're dealing with here. Otherwise, assume direct * access. */ if (inq_data == NULL) { pd_type = T_DIRECT; match = NULL; } else { pd_type = SID_TYPE(inq_data); match = cam_quirkmatch((caddr_t)inq_data, (caddr_t)scsi_op_quirk_table, nitems(scsi_op_quirk_table), sizeof(*scsi_op_quirk_table), scsi_inquiry_match); } if (match != NULL) { table[0] = ((struct scsi_op_quirk_entry *)match)->op_table; num_ops[0] = ((struct scsi_op_quirk_entry *)match)->num_ops; table[1] = scsi_op_codes; num_ops[1] = nitems(scsi_op_codes); num_tables = 2; } else { /* * If this is true, we have a vendor specific opcode that * wasn't covered in the quirk table. */ if ((opcode > 0xBF) || ((opcode > 0x5F) && (opcode < 0x80))) return("Vendor Specific Command"); table[0] = scsi_op_codes; num_ops[0] = nitems(scsi_op_codes); num_tables = 1; } /* RBC is 'Simplified' Direct Access Device */ if (pd_type == T_RBC) pd_type = T_DIRECT; /* * Host managed drives are direct access for the most part. */ if (pd_type == T_ZBC_HM) pd_type = T_DIRECT; /* Map NODEVICE to Direct Access Device to handle REPORT LUNS, etc. */ if (pd_type == T_NODEVICE) pd_type = T_DIRECT; opmask = 1 << pd_type; for (j = 0; j < num_tables; j++) { for (i = 0;i < num_ops[j] && table[j][i].opcode <= opcode; i++){ if ((table[j][i].opcode == opcode) && ((table[j][i].opmask & opmask) != 0)) return(table[j][i].desc); } } /* * If we can't find a match for the command in the table, we just * assume it's a vendor specifc command. */ return("Vendor Specific Command"); } #else /* SCSI_NO_OP_STRINGS */ const char * scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data) { return(""); } #endif #if !defined(SCSI_NO_SENSE_STRINGS) #define SST(asc, ascq, action, desc) \ asc, ascq, action, desc #else const char empty_string[] = ""; #define SST(asc, ascq, action, desc) \ asc, ascq, action, empty_string #endif const struct sense_key_table_entry sense_key_table[] = { { SSD_KEY_NO_SENSE, SS_NOP, "NO SENSE" }, { SSD_KEY_RECOVERED_ERROR, SS_NOP|SSQ_PRINT_SENSE, "RECOVERED ERROR" }, { SSD_KEY_NOT_READY, SS_RDEF, "NOT READY" }, { SSD_KEY_MEDIUM_ERROR, SS_RDEF, "MEDIUM ERROR" }, { SSD_KEY_HARDWARE_ERROR, SS_RDEF, "HARDWARE FAILURE" }, { SSD_KEY_ILLEGAL_REQUEST, SS_FATAL|EINVAL, "ILLEGAL REQUEST" }, { SSD_KEY_UNIT_ATTENTION, SS_FATAL|ENXIO, "UNIT ATTENTION" }, { SSD_KEY_DATA_PROTECT, SS_FATAL|EACCES, "DATA PROTECT" }, { SSD_KEY_BLANK_CHECK, SS_FATAL|ENOSPC, "BLANK CHECK" }, { SSD_KEY_Vendor_Specific, SS_FATAL|EIO, "Vendor Specific" }, { SSD_KEY_COPY_ABORTED, SS_FATAL|EIO, "COPY ABORTED" }, { SSD_KEY_ABORTED_COMMAND, SS_RDEF, "ABORTED COMMAND" }, { SSD_KEY_EQUAL, SS_NOP, "EQUAL" }, { SSD_KEY_VOLUME_OVERFLOW, SS_FATAL|EIO, "VOLUME OVERFLOW" }, { SSD_KEY_MISCOMPARE, SS_NOP, "MISCOMPARE" }, { SSD_KEY_COMPLETED, SS_NOP, "COMPLETED" } }; static struct asc_table_entry quantum_fireball_entries[] = { { SST(0x04, 0x0b, SS_START | SSQ_DECREMENT_COUNT | ENXIO, "Logical unit not ready, initializing cmd. required") } }; static struct asc_table_entry sony_mo_entries[] = { { SST(0x04, 0x00, SS_START | SSQ_DECREMENT_COUNT | ENXIO, "Logical unit not ready, cause not reportable") } }; static struct asc_table_entry hgst_entries[] = { { SST(0x04, 0xF0, SS_RDEF, "Vendor Unique - Logical Unit Not Ready") }, { SST(0x0A, 0x01, SS_RDEF, "Unrecovered Super Certification Log Write Error") }, { SST(0x0A, 0x02, SS_RDEF, "Unrecovered Super Certification Log Read Error") }, { SST(0x15, 0x03, SS_RDEF, "Unrecovered Sector Error") }, { SST(0x3E, 0x04, SS_RDEF, "Unrecovered Self-Test Hard-Cache Test Fail") }, { SST(0x3E, 0x05, SS_RDEF, "Unrecovered Self-Test OTF-Cache Fail") }, { SST(0x40, 0x00, SS_RDEF, "Unrecovered SAT No Buffer Overflow Error") }, { SST(0x40, 0x01, SS_RDEF, "Unrecovered SAT Buffer Overflow Error") }, { SST(0x40, 0x02, SS_RDEF, "Unrecovered SAT No Buffer Overflow With ECS Fault") }, { SST(0x40, 0x03, SS_RDEF, "Unrecovered SAT Buffer Overflow With ECS Fault") }, { SST(0x40, 0x81, SS_RDEF, "DRAM Failure") }, { SST(0x44, 0x0B, SS_RDEF, "Vendor Unique - Internal Target Failure") }, { SST(0x44, 0xF2, SS_RDEF, "Vendor Unique - Internal Target Failure") }, { SST(0x44, 0xF6, SS_RDEF, "Vendor Unique - Internal Target Failure") }, { SST(0x44, 0xF9, SS_RDEF, "Vendor Unique - Internal Target Failure") }, { SST(0x44, 0xFA, SS_RDEF, "Vendor Unique - Internal Target Failure") }, { SST(0x5D, 0x22, SS_RDEF, "Extreme Over-Temperature Warning") }, { SST(0x5D, 0x50, SS_RDEF, "Load/Unload cycle Count Warning") }, { SST(0x81, 0x00, SS_RDEF, "Vendor Unique - Internal Logic Error") }, { SST(0x85, 0x00, SS_RDEF, "Vendor Unique - Internal Key Seed Error") }, }; static struct asc_table_entry seagate_entries[] = { { SST(0x04, 0xF0, SS_RDEF, "Logical Unit Not Ready, super certify in Progress") }, { SST(0x08, 0x86, SS_RDEF, "Write Fault Data Corruption") }, { SST(0x09, 0x0D, SS_RDEF, "Tracking Failure") }, { SST(0x09, 0x0E, SS_RDEF, "ETF Failure") }, { SST(0x0B, 0x5D, SS_RDEF, "Pre-SMART Warning") }, { SST(0x0B, 0x85, SS_RDEF, "5V Voltage Warning") }, { SST(0x0B, 0x8C, SS_RDEF, "12V Voltage Warning") }, { SST(0x0C, 0xFF, SS_RDEF, "Write Error - Too many error recovery revs") }, { SST(0x11, 0xFF, SS_RDEF, "Unrecovered Read Error - Too many error recovery revs") }, { SST(0x19, 0x0E, SS_RDEF, "Fewer than 1/2 defect list copies") }, { SST(0x20, 0xF3, SS_RDEF, "Illegal CDB linked to skip mask cmd") }, { SST(0x24, 0xF0, SS_RDEF, "Illegal byte in CDB, LBA not matching") }, { SST(0x24, 0xF1, SS_RDEF, "Illegal byte in CDB, LEN not matching") }, { SST(0x24, 0xF2, SS_RDEF, "Mask not matching transfer length") }, { SST(0x24, 0xF3, SS_RDEF, "Drive formatted without plist") }, { SST(0x26, 0x95, SS_RDEF, "Invalid Field Parameter - CAP File") }, { SST(0x26, 0x96, SS_RDEF, "Invalid Field Parameter - RAP File") }, { SST(0x26, 0x97, SS_RDEF, "Invalid Field Parameter - TMS Firmware Tag") }, { SST(0x26, 0x98, SS_RDEF, "Invalid Field Parameter - Check Sum") }, { SST(0x26, 0x99, SS_RDEF, "Invalid Field Parameter - Firmware Tag") }, { SST(0x29, 0x08, SS_RDEF, "Write Log Dump data") }, { SST(0x29, 0x09, SS_RDEF, "Write Log Dump data") }, { SST(0x29, 0x0A, SS_RDEF, "Reserved disk space") }, { SST(0x29, 0x0B, SS_RDEF, "SDBP") }, { SST(0x29, 0x0C, SS_RDEF, "SDBP") }, { SST(0x31, 0x91, SS_RDEF, "Format Corrupted World Wide Name (WWN) is Invalid") }, { SST(0x32, 0x03, SS_RDEF, "Defect List - Length exceeds Command Allocated Length") }, { SST(0x33, 0x00, SS_RDEF, "Flash not ready for access") }, { SST(0x3F, 0x70, SS_RDEF, "Invalid RAP block") }, { SST(0x3F, 0x71, SS_RDEF, "RAP/ETF mismatch") }, { SST(0x3F, 0x90, SS_RDEF, "Invalid CAP block") }, { SST(0x3F, 0x91, SS_RDEF, "World Wide Name (WWN) Mismatch") }, { SST(0x40, 0x01, SS_RDEF, "DRAM Parity Error") }, { SST(0x40, 0x02, SS_RDEF, "DRAM Parity Error") }, { SST(0x42, 0x0A, SS_RDEF, "Loopback Test") }, { SST(0x42, 0x0B, SS_RDEF, "Loopback Test") }, { SST(0x44, 0xF2, SS_RDEF, "Compare error during data integrity check") }, { SST(0x44, 0xF6, SS_RDEF, "Unrecoverable error during data integrity check") }, { SST(0x47, 0x80, SS_RDEF, "Fibre Channel Sequence Error") }, { SST(0x4E, 0x01, SS_RDEF, "Information Unit Too Short") }, { SST(0x80, 0x00, SS_RDEF, "General Firmware Error / Command Timeout") }, { SST(0x80, 0x01, SS_RDEF, "Command Timeout") }, { SST(0x80, 0x02, SS_RDEF, "Command Timeout") }, { SST(0x80, 0x80, SS_RDEF, "FC FIFO Error During Read Transfer") }, { SST(0x80, 0x81, SS_RDEF, "FC FIFO Error During Write Transfer") }, { SST(0x80, 0x82, SS_RDEF, "DISC FIFO Error During Read Transfer") }, { SST(0x80, 0x83, SS_RDEF, "DISC FIFO Error During Write Transfer") }, { SST(0x80, 0x84, SS_RDEF, "LBA Seeded LRC Error on Read") }, { SST(0x80, 0x85, SS_RDEF, "LBA Seeded LRC Error on Write") }, { SST(0x80, 0x86, SS_RDEF, "IOEDC Error on Read") }, { SST(0x80, 0x87, SS_RDEF, "IOEDC Error on Write") }, { SST(0x80, 0x88, SS_RDEF, "Host Parity Check Failed") }, { SST(0x80, 0x89, SS_RDEF, "IOEDC error on read detected by formatter") }, { SST(0x80, 0x8A, SS_RDEF, "Host Parity Errors / Host FIFO Initialization Failed") }, { SST(0x80, 0x8B, SS_RDEF, "Host Parity Errors") }, { SST(0x80, 0x8C, SS_RDEF, "Host Parity Errors") }, { SST(0x80, 0x8D, SS_RDEF, "Host Parity Errors") }, { SST(0x81, 0x00, SS_RDEF, "LA Check Failed") }, { SST(0x82, 0x00, SS_RDEF, "Internal client detected insufficient buffer") }, { SST(0x84, 0x00, SS_RDEF, "Scheduled Diagnostic And Repair") }, }; static struct scsi_sense_quirk_entry sense_quirk_table[] = { { /* * XXX The Quantum Fireball ST and SE like to return 0x04 0x0b * when they really should return 0x04 0x02. */ {T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "FIREBALL S*", "*"}, /*num_sense_keys*/0, nitems(quantum_fireball_entries), /*sense key entries*/NULL, quantum_fireball_entries }, { /* * This Sony MO drive likes to return 0x04, 0x00 when it * isn't spun up. */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SONY", "SMO-*", "*"}, /*num_sense_keys*/0, nitems(sony_mo_entries), /*sense key entries*/NULL, sony_mo_entries }, { /* * HGST vendor-specific error codes */ {T_DIRECT, SIP_MEDIA_FIXED, "HGST", "*", "*"}, /*num_sense_keys*/0, nitems(hgst_entries), /*sense key entries*/NULL, hgst_entries }, { /* * SEAGATE vendor-specific error codes */ {T_DIRECT, SIP_MEDIA_FIXED, "SEAGATE", "*", "*"}, /*num_sense_keys*/0, nitems(seagate_entries), /*sense key entries*/NULL, seagate_entries } }; const u_int sense_quirk_table_size = nitems(sense_quirk_table); static struct asc_table_entry asc_table[] = { /* * From: http://www.t10.org/lists/asc-num.txt * Modifications by Jung-uk Kim (jkim@FreeBSD.org) */ /* * File: ASC-NUM.TXT * * SCSI ASC/ASCQ Assignments * Numeric Sorted Listing * as of 8/12/15 * * D - DIRECT ACCESS DEVICE (SBC-2) device column key * .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- * . L - PRINTER DEVICE (SSC) blank = reserved * . P - PROCESSOR DEVICE (SPC) not blank = allowed * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC-2) * . . R - CD DEVICE (MMC) * . . O - OPTICAL MEMORY DEVICE (SBC-2) * . . .M - MEDIA CHANGER DEVICE (SMC) * . . . A - STORAGE ARRAY DEVICE (SCC) * . . . E - ENCLOSURE SERVICES DEVICE (SES) * . . . .B - SIMPLIFIED DIRECT-ACCESS DEVICE (RBC) * . . . . K - OPTICAL CARD READER/WRITER DEVICE (OCRW) * . . . . V - AUTOMATION/DRIVE INTERFACE (ADC) * . . . . .F - OBJECT-BASED STORAGE (OSD) * DTLPWROMAEBKVF * ASC ASCQ Action * Description */ /* DTLPWROMAEBKVF */ { SST(0x00, 0x00, SS_NOP, "No additional sense information") }, /* T */ { SST(0x00, 0x01, SS_RDEF, "Filemark detected") }, /* T */ { SST(0x00, 0x02, SS_RDEF, "End-of-partition/medium detected") }, /* T */ { SST(0x00, 0x03, SS_RDEF, "Setmark detected") }, /* T */ { SST(0x00, 0x04, SS_RDEF, "Beginning-of-partition/medium detected") }, /* TL */ { SST(0x00, 0x05, SS_RDEF, "End-of-data detected") }, /* DTLPWROMAEBKVF */ { SST(0x00, 0x06, SS_RDEF, "I/O process terminated") }, /* T */ { SST(0x00, 0x07, SS_RDEF, /* XXX TBD */ "Programmable early warning detected") }, /* R */ { SST(0x00, 0x11, SS_FATAL | EBUSY, "Audio play operation in progress") }, /* R */ { SST(0x00, 0x12, SS_NOP, "Audio play operation paused") }, /* R */ { SST(0x00, 0x13, SS_NOP, "Audio play operation successfully completed") }, /* R */ { SST(0x00, 0x14, SS_RDEF, "Audio play operation stopped due to error") }, /* R */ { SST(0x00, 0x15, SS_NOP, "No current audio status to return") }, /* DTLPWROMAEBKVF */ { SST(0x00, 0x16, SS_FATAL | EBUSY, "Operation in progress") }, /* DTL WROMAEBKVF */ { SST(0x00, 0x17, SS_RDEF, "Cleaning requested") }, /* T */ { SST(0x00, 0x18, SS_RDEF, /* XXX TBD */ "Erase operation in progress") }, /* T */ { SST(0x00, 0x19, SS_RDEF, /* XXX TBD */ "Locate operation in progress") }, /* T */ { SST(0x00, 0x1A, SS_RDEF, /* XXX TBD */ "Rewind operation in progress") }, /* T */ { SST(0x00, 0x1B, SS_RDEF, /* XXX TBD */ "Set capacity operation in progress") }, /* T */ { SST(0x00, 0x1C, SS_RDEF, /* XXX TBD */ "Verify operation in progress") }, /* DT B */ { SST(0x00, 0x1D, SS_NOP, "ATA pass through information available") }, /* DT R MAEBKV */ { SST(0x00, 0x1E, SS_RDEF, /* XXX TBD */ "Conflicting SA creation request") }, /* DT B */ { SST(0x00, 0x1F, SS_RDEF, /* XXX TBD */ "Logical unit transitioning to another power condition") }, /* DT P B */ { SST(0x00, 0x20, SS_NOP, "Extended copy information available") }, /* D */ { SST(0x00, 0x21, SS_RDEF, /* XXX TBD */ "Atomic command aborted due to ACA") }, /* D W O BK */ { SST(0x01, 0x00, SS_RDEF, "No index/sector signal") }, /* D WRO BK */ { SST(0x02, 0x00, SS_RDEF, "No seek complete") }, /* DTL W O BK */ { SST(0x03, 0x00, SS_RDEF, "Peripheral device write fault") }, /* T */ { SST(0x03, 0x01, SS_RDEF, "No write current") }, /* T */ { SST(0x03, 0x02, SS_RDEF, "Excessive write errors") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x00, SS_RDEF, "Logical unit not ready, cause not reportable") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x01, SS_WAIT | EBUSY, "Logical unit is in process of becoming ready") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x02, SS_START | SSQ_DECREMENT_COUNT | ENXIO, "Logical unit not ready, initializing command required") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x03, SS_FATAL | ENXIO, "Logical unit not ready, manual intervention required") }, /* DTL RO B */ { SST(0x04, 0x04, SS_FATAL | EBUSY, "Logical unit not ready, format in progress") }, /* DT W O A BK F */ { SST(0x04, 0x05, SS_FATAL | EBUSY, "Logical unit not ready, rebuild in progress") }, /* DT W O A BK */ { SST(0x04, 0x06, SS_FATAL | EBUSY, "Logical unit not ready, recalculation in progress") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x07, SS_FATAL | EBUSY, "Logical unit not ready, operation in progress") }, /* R */ { SST(0x04, 0x08, SS_FATAL | EBUSY, "Logical unit not ready, long write in progress") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x09, SS_RDEF, /* XXX TBD */ "Logical unit not ready, self-test in progress") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x0A, SS_WAIT | ENXIO, "Logical unit not accessible, asymmetric access state transition")}, /* DTLPWROMAEBKVF */ { SST(0x04, 0x0B, SS_FATAL | ENXIO, "Logical unit not accessible, target port in standby state") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x0C, SS_FATAL | ENXIO, "Logical unit not accessible, target port in unavailable state") }, /* F */ { SST(0x04, 0x0D, SS_RDEF, /* XXX TBD */ "Logical unit not ready, structure check required") }, /* DTL WR MAEBKVF */ { SST(0x04, 0x0E, SS_RDEF, /* XXX TBD */ "Logical unit not ready, security session in progress") }, /* DT WROM B */ { SST(0x04, 0x10, SS_RDEF, /* XXX TBD */ "Logical unit not ready, auxiliary memory not accessible") }, /* DT WRO AEB VF */ { SST(0x04, 0x11, SS_WAIT | EBUSY, "Logical unit not ready, notify (enable spinup) required") }, /* M V */ { SST(0x04, 0x12, SS_RDEF, /* XXX TBD */ "Logical unit not ready, offline") }, /* DT R MAEBKV */ { SST(0x04, 0x13, SS_RDEF, /* XXX TBD */ "Logical unit not ready, SA creation in progress") }, /* D B */ { SST(0x04, 0x14, SS_RDEF, /* XXX TBD */ "Logical unit not ready, space allocation in progress") }, /* M */ { SST(0x04, 0x15, SS_RDEF, /* XXX TBD */ "Logical unit not ready, robotics disabled") }, /* M */ { SST(0x04, 0x16, SS_RDEF, /* XXX TBD */ "Logical unit not ready, configuration required") }, /* M */ { SST(0x04, 0x17, SS_RDEF, /* XXX TBD */ "Logical unit not ready, calibration required") }, /* M */ { SST(0x04, 0x18, SS_RDEF, /* XXX TBD */ "Logical unit not ready, a door is open") }, /* M */ { SST(0x04, 0x19, SS_RDEF, /* XXX TBD */ "Logical unit not ready, operating in sequential mode") }, /* DT B */ { SST(0x04, 0x1A, SS_RDEF, /* XXX TBD */ "Logical unit not ready, START/STOP UNIT command in progress") }, /* D B */ { SST(0x04, 0x1B, SS_RDEF, /* XXX TBD */ "Logical unit not ready, sanitize in progress") }, /* DT MAEB */ - { SST(0x04, 0x1C, SS_RDEF, /* XXX TBD */ + { SST(0x04, 0x1C, SS_START | SSQ_DECREMENT_COUNT | ENXIO, "Logical unit not ready, additional power use not yet granted") }, /* D */ { SST(0x04, 0x1D, SS_RDEF, /* XXX TBD */ "Logical unit not ready, configuration in progress") }, /* D */ { SST(0x04, 0x1E, SS_FATAL | ENXIO, "Logical unit not ready, microcode activation required") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x1F, SS_FATAL | ENXIO, "Logical unit not ready, microcode download required") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x20, SS_RDEF, /* XXX TBD */ "Logical unit not ready, logical unit reset required") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x21, SS_RDEF, /* XXX TBD */ "Logical unit not ready, hard reset required") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x22, SS_RDEF, /* XXX TBD */ "Logical unit not ready, power cycle required") }, /* DTL WROMAEBKVF */ { SST(0x05, 0x00, SS_RDEF, "Logical unit does not respond to selection") }, /* D WROM BK */ { SST(0x06, 0x00, SS_RDEF, "No reference position found") }, /* DTL WROM BK */ { SST(0x07, 0x00, SS_RDEF, "Multiple peripheral devices selected") }, /* DTL WROMAEBKVF */ { SST(0x08, 0x00, SS_RDEF, "Logical unit communication failure") }, /* DTL WROMAEBKVF */ { SST(0x08, 0x01, SS_RDEF, "Logical unit communication time-out") }, /* DTL WROMAEBKVF */ { SST(0x08, 0x02, SS_RDEF, "Logical unit communication parity error") }, /* DT ROM BK */ { SST(0x08, 0x03, SS_RDEF, "Logical unit communication CRC error (Ultra-DMA/32)") }, /* DTLPWRO K */ { SST(0x08, 0x04, SS_RDEF, /* XXX TBD */ "Unreachable copy target") }, /* DT WRO B */ { SST(0x09, 0x00, SS_RDEF, "Track following error") }, /* WRO K */ { SST(0x09, 0x01, SS_RDEF, "Tracking servo failure") }, /* WRO K */ { SST(0x09, 0x02, SS_RDEF, "Focus servo failure") }, /* WRO */ { SST(0x09, 0x03, SS_RDEF, "Spindle servo failure") }, /* DT WRO B */ { SST(0x09, 0x04, SS_RDEF, "Head select fault") }, /* DT RO B */ { SST(0x09, 0x05, SS_RDEF, "Vibration induced tracking error") }, /* DTLPWROMAEBKVF */ { SST(0x0A, 0x00, SS_FATAL | ENOSPC, "Error log overflow") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x00, SS_NOP | SSQ_PRINT_SENSE, "Warning") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x01, SS_NOP | SSQ_PRINT_SENSE, "Warning - specified temperature exceeded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x02, SS_NOP | SSQ_PRINT_SENSE, "Warning - enclosure degraded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x03, SS_NOP | SSQ_PRINT_SENSE, "Warning - background self-test failed") }, /* DTLPWRO AEBKVF */ { SST(0x0B, 0x04, SS_NOP | SSQ_PRINT_SENSE, "Warning - background pre-scan detected medium error") }, /* DTLPWRO AEBKVF */ { SST(0x0B, 0x05, SS_NOP | SSQ_PRINT_SENSE, "Warning - background medium scan detected medium error") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x06, SS_NOP | SSQ_PRINT_SENSE, "Warning - non-volatile cache now volatile") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x07, SS_NOP | SSQ_PRINT_SENSE, "Warning - degraded power to non-volatile cache") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x08, SS_NOP | SSQ_PRINT_SENSE, "Warning - power loss expected") }, /* D */ { SST(0x0B, 0x09, SS_NOP | SSQ_PRINT_SENSE, "Warning - device statistics notification available") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x0A, SS_NOP | SSQ_PRINT_SENSE, "Warning - High critical temperature limit exceeded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x0B, SS_NOP | SSQ_PRINT_SENSE, "Warning - Low critical temperature limit exceeded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x0C, SS_NOP | SSQ_PRINT_SENSE, "Warning - High operating temperature limit exceeded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x0D, SS_NOP | SSQ_PRINT_SENSE, "Warning - Low operating temperature limit exceeded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x0E, SS_NOP | SSQ_PRINT_SENSE, "Warning - High citical humidity limit exceeded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x0F, SS_NOP | SSQ_PRINT_SENSE, "Warning - Low citical humidity limit exceeded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x10, SS_NOP | SSQ_PRINT_SENSE, "Warning - High operating humidity limit exceeded") }, /* DTLPWROMAEBKVF */ { SST(0x0B, 0x11, SS_NOP | SSQ_PRINT_SENSE, "Warning - Low operating humidity limit exceeded") }, /* T R */ { SST(0x0C, 0x00, SS_RDEF, "Write error") }, /* K */ { SST(0x0C, 0x01, SS_NOP | SSQ_PRINT_SENSE, "Write error - recovered with auto reallocation") }, /* D W O BK */ { SST(0x0C, 0x02, SS_RDEF, "Write error - auto reallocation failed") }, /* D W O BK */ { SST(0x0C, 0x03, SS_RDEF, "Write error - recommend reassignment") }, /* DT W O B */ { SST(0x0C, 0x04, SS_RDEF, "Compression check miscompare error") }, /* DT W O B */ { SST(0x0C, 0x05, SS_RDEF, "Data expansion occurred during compression") }, /* DT W O B */ { SST(0x0C, 0x06, SS_RDEF, "Block not compressible") }, /* R */ { SST(0x0C, 0x07, SS_RDEF, "Write error - recovery needed") }, /* R */ { SST(0x0C, 0x08, SS_RDEF, "Write error - recovery failed") }, /* R */ { SST(0x0C, 0x09, SS_RDEF, "Write error - loss of streaming") }, /* R */ { SST(0x0C, 0x0A, SS_RDEF, "Write error - padding blocks added") }, /* DT WROM B */ { SST(0x0C, 0x0B, SS_RDEF, /* XXX TBD */ "Auxiliary memory write error") }, /* DTLPWRO AEBKVF */ { SST(0x0C, 0x0C, SS_RDEF, /* XXX TBD */ "Write error - unexpected unsolicited data") }, /* DTLPWRO AEBKVF */ { SST(0x0C, 0x0D, SS_RDEF, /* XXX TBD */ "Write error - not enough unsolicited data") }, /* DT W O BK */ { SST(0x0C, 0x0E, SS_RDEF, /* XXX TBD */ "Multiple write errors") }, /* R */ { SST(0x0C, 0x0F, SS_RDEF, /* XXX TBD */ "Defects in error window") }, /* D */ { SST(0x0C, 0x10, SS_RDEF, /* XXX TBD */ "Incomplete multiple atomic write operations") }, /* D */ { SST(0x0C, 0x11, SS_RDEF, /* XXX TBD */ "Write error - recovery scan needed") }, /* D */ { SST(0x0C, 0x12, SS_RDEF, /* XXX TBD */ "Write error - insufficient zone resources") }, /* DTLPWRO A K */ { SST(0x0D, 0x00, SS_RDEF, /* XXX TBD */ "Error detected by third party temporary initiator") }, /* DTLPWRO A K */ { SST(0x0D, 0x01, SS_RDEF, /* XXX TBD */ "Third party device failure") }, /* DTLPWRO A K */ { SST(0x0D, 0x02, SS_RDEF, /* XXX TBD */ "Copy target device not reachable") }, /* DTLPWRO A K */ { SST(0x0D, 0x03, SS_RDEF, /* XXX TBD */ "Incorrect copy target device type") }, /* DTLPWRO A K */ { SST(0x0D, 0x04, SS_RDEF, /* XXX TBD */ "Copy target device data underrun") }, /* DTLPWRO A K */ { SST(0x0D, 0x05, SS_RDEF, /* XXX TBD */ "Copy target device data overrun") }, /* DT PWROMAEBK F */ { SST(0x0E, 0x00, SS_RDEF, /* XXX TBD */ "Invalid information unit") }, /* DT PWROMAEBK F */ { SST(0x0E, 0x01, SS_RDEF, /* XXX TBD */ "Information unit too short") }, /* DT PWROMAEBK F */ { SST(0x0E, 0x02, SS_RDEF, /* XXX TBD */ "Information unit too long") }, /* DT P R MAEBK F */ { SST(0x0E, 0x03, SS_FATAL | EINVAL, "Invalid field in command information unit") }, /* D W O BK */ { SST(0x10, 0x00, SS_RDEF, "ID CRC or ECC error") }, /* DT W O */ { SST(0x10, 0x01, SS_RDEF, /* XXX TBD */ "Logical block guard check failed") }, /* DT W O */ { SST(0x10, 0x02, SS_RDEF, /* XXX TBD */ "Logical block application tag check failed") }, /* DT W O */ { SST(0x10, 0x03, SS_RDEF, /* XXX TBD */ "Logical block reference tag check failed") }, /* T */ { SST(0x10, 0x04, SS_RDEF, /* XXX TBD */ "Logical block protection error on recovered buffer data") }, /* T */ { SST(0x10, 0x05, SS_RDEF, /* XXX TBD */ "Logical block protection method error") }, /* DT WRO BK */ { SST(0x11, 0x00, SS_FATAL|EIO, "Unrecovered read error") }, /* DT WRO BK */ { SST(0x11, 0x01, SS_FATAL|EIO, "Read retries exhausted") }, /* DT WRO BK */ { SST(0x11, 0x02, SS_FATAL|EIO, "Error too long to correct") }, /* DT W O BK */ { SST(0x11, 0x03, SS_FATAL|EIO, "Multiple read errors") }, /* D W O BK */ { SST(0x11, 0x04, SS_FATAL|EIO, "Unrecovered read error - auto reallocate failed") }, /* WRO B */ { SST(0x11, 0x05, SS_FATAL|EIO, "L-EC uncorrectable error") }, /* WRO B */ { SST(0x11, 0x06, SS_FATAL|EIO, "CIRC unrecovered error") }, /* W O B */ { SST(0x11, 0x07, SS_RDEF, "Data re-synchronization error") }, /* T */ { SST(0x11, 0x08, SS_RDEF, "Incomplete block read") }, /* T */ { SST(0x11, 0x09, SS_RDEF, "No gap found") }, /* DT O BK */ { SST(0x11, 0x0A, SS_RDEF, "Miscorrected error") }, /* D W O BK */ { SST(0x11, 0x0B, SS_FATAL|EIO, "Unrecovered read error - recommend reassignment") }, /* D W O BK */ { SST(0x11, 0x0C, SS_FATAL|EIO, "Unrecovered read error - recommend rewrite the data") }, /* DT WRO B */ { SST(0x11, 0x0D, SS_RDEF, "De-compression CRC error") }, /* DT WRO B */ { SST(0x11, 0x0E, SS_RDEF, "Cannot decompress using declared algorithm") }, /* R */ { SST(0x11, 0x0F, SS_RDEF, "Error reading UPC/EAN number") }, /* R */ { SST(0x11, 0x10, SS_RDEF, "Error reading ISRC number") }, /* R */ { SST(0x11, 0x11, SS_RDEF, "Read error - loss of streaming") }, /* DT WROM B */ { SST(0x11, 0x12, SS_RDEF, /* XXX TBD */ "Auxiliary memory read error") }, /* DTLPWRO AEBKVF */ { SST(0x11, 0x13, SS_RDEF, /* XXX TBD */ "Read error - failed retransmission request") }, /* D */ { SST(0x11, 0x14, SS_RDEF, /* XXX TBD */ "Read error - LBA marked bad by application client") }, /* D */ { SST(0x11, 0x15, SS_RDEF, /* XXX TBD */ "Write after sanitize required") }, /* D W O BK */ { SST(0x12, 0x00, SS_RDEF, "Address mark not found for ID field") }, /* D W O BK */ { SST(0x13, 0x00, SS_RDEF, "Address mark not found for data field") }, /* DTL WRO BK */ { SST(0x14, 0x00, SS_RDEF, "Recorded entity not found") }, /* DT WRO BK */ { SST(0x14, 0x01, SS_RDEF, "Record not found") }, /* T */ { SST(0x14, 0x02, SS_RDEF, "Filemark or setmark not found") }, /* T */ { SST(0x14, 0x03, SS_RDEF, "End-of-data not found") }, /* T */ { SST(0x14, 0x04, SS_RDEF, "Block sequence error") }, /* DT W O BK */ { SST(0x14, 0x05, SS_RDEF, "Record not found - recommend reassignment") }, /* DT W O BK */ { SST(0x14, 0x06, SS_RDEF, "Record not found - data auto-reallocated") }, /* T */ { SST(0x14, 0x07, SS_RDEF, /* XXX TBD */ "Locate operation failure") }, /* DTL WROM BK */ { SST(0x15, 0x00, SS_RDEF, "Random positioning error") }, /* DTL WROM BK */ { SST(0x15, 0x01, SS_RDEF, "Mechanical positioning error") }, /* DT WRO BK */ { SST(0x15, 0x02, SS_RDEF, "Positioning error detected by read of medium") }, /* D W O BK */ { SST(0x16, 0x00, SS_RDEF, "Data synchronization mark error") }, /* D W O BK */ { SST(0x16, 0x01, SS_RDEF, "Data sync error - data rewritten") }, /* D W O BK */ { SST(0x16, 0x02, SS_RDEF, "Data sync error - recommend rewrite") }, /* D W O BK */ { SST(0x16, 0x03, SS_NOP | SSQ_PRINT_SENSE, "Data sync error - data auto-reallocated") }, /* D W O BK */ { SST(0x16, 0x04, SS_RDEF, "Data sync error - recommend reassignment") }, /* DT WRO BK */ { SST(0x17, 0x00, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with no error correction applied") }, /* DT WRO BK */ { SST(0x17, 0x01, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with retries") }, /* DT WRO BK */ { SST(0x17, 0x02, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with positive head offset") }, /* DT WRO BK */ { SST(0x17, 0x03, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with negative head offset") }, /* WRO B */ { SST(0x17, 0x04, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with retries and/or CIRC applied") }, /* D WRO BK */ { SST(0x17, 0x05, SS_NOP | SSQ_PRINT_SENSE, "Recovered data using previous sector ID") }, /* D W O BK */ { SST(0x17, 0x06, SS_NOP | SSQ_PRINT_SENSE, "Recovered data without ECC - data auto-reallocated") }, /* D WRO BK */ { SST(0x17, 0x07, SS_NOP | SSQ_PRINT_SENSE, "Recovered data without ECC - recommend reassignment") }, /* D WRO BK */ { SST(0x17, 0x08, SS_NOP | SSQ_PRINT_SENSE, "Recovered data without ECC - recommend rewrite") }, /* D WRO BK */ { SST(0x17, 0x09, SS_NOP | SSQ_PRINT_SENSE, "Recovered data without ECC - data rewritten") }, /* DT WRO BK */ { SST(0x18, 0x00, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with error correction applied") }, /* D WRO BK */ { SST(0x18, 0x01, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with error corr. & retries applied") }, /* D WRO BK */ { SST(0x18, 0x02, SS_NOP | SSQ_PRINT_SENSE, "Recovered data - data auto-reallocated") }, /* R */ { SST(0x18, 0x03, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with CIRC") }, /* R */ { SST(0x18, 0x04, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with L-EC") }, /* D WRO BK */ { SST(0x18, 0x05, SS_NOP | SSQ_PRINT_SENSE, "Recovered data - recommend reassignment") }, /* D WRO BK */ { SST(0x18, 0x06, SS_NOP | SSQ_PRINT_SENSE, "Recovered data - recommend rewrite") }, /* D W O BK */ { SST(0x18, 0x07, SS_NOP | SSQ_PRINT_SENSE, "Recovered data with ECC - data rewritten") }, /* R */ { SST(0x18, 0x08, SS_RDEF, /* XXX TBD */ "Recovered data with linking") }, /* D O K */ { SST(0x19, 0x00, SS_RDEF, "Defect list error") }, /* D O K */ { SST(0x19, 0x01, SS_RDEF, "Defect list not available") }, /* D O K */ { SST(0x19, 0x02, SS_RDEF, "Defect list error in primary list") }, /* D O K */ { SST(0x19, 0x03, SS_RDEF, "Defect list error in grown list") }, /* DTLPWROMAEBKVF */ { SST(0x1A, 0x00, SS_RDEF, "Parameter list length error") }, /* DTLPWROMAEBKVF */ { SST(0x1B, 0x00, SS_RDEF, "Synchronous data transfer error") }, /* D O BK */ { SST(0x1C, 0x00, SS_RDEF, "Defect list not found") }, /* D O BK */ { SST(0x1C, 0x01, SS_RDEF, "Primary defect list not found") }, /* D O BK */ { SST(0x1C, 0x02, SS_RDEF, "Grown defect list not found") }, /* DT WRO BK */ { SST(0x1D, 0x00, SS_FATAL, "Miscompare during verify operation") }, /* D B */ { SST(0x1D, 0x01, SS_RDEF, /* XXX TBD */ "Miscomparable verify of unmapped LBA") }, /* D W O BK */ { SST(0x1E, 0x00, SS_NOP | SSQ_PRINT_SENSE, "Recovered ID with ECC correction") }, /* D O K */ { SST(0x1F, 0x00, SS_RDEF, "Partial defect list transfer") }, /* DTLPWROMAEBKVF */ { SST(0x20, 0x00, SS_FATAL | EINVAL, "Invalid command operation code") }, /* DT PWROMAEBK */ { SST(0x20, 0x01, SS_RDEF, /* XXX TBD */ "Access denied - initiator pending-enrolled") }, /* DT PWROMAEBK */ { SST(0x20, 0x02, SS_FATAL | EPERM, "Access denied - no access rights") }, /* DT PWROMAEBK */ { SST(0x20, 0x03, SS_RDEF, /* XXX TBD */ "Access denied - invalid mgmt ID key") }, /* T */ { SST(0x20, 0x04, SS_RDEF, /* XXX TBD */ "Illegal command while in write capable state") }, /* T */ { SST(0x20, 0x05, SS_RDEF, /* XXX TBD */ "Obsolete") }, /* T */ { SST(0x20, 0x06, SS_RDEF, /* XXX TBD */ "Illegal command while in explicit address mode") }, /* T */ { SST(0x20, 0x07, SS_RDEF, /* XXX TBD */ "Illegal command while in implicit address mode") }, /* DT PWROMAEBK */ { SST(0x20, 0x08, SS_RDEF, /* XXX TBD */ "Access denied - enrollment conflict") }, /* DT PWROMAEBK */ { SST(0x20, 0x09, SS_RDEF, /* XXX TBD */ "Access denied - invalid LU identifier") }, /* DT PWROMAEBK */ { SST(0x20, 0x0A, SS_RDEF, /* XXX TBD */ "Access denied - invalid proxy token") }, /* DT PWROMAEBK */ { SST(0x20, 0x0B, SS_RDEF, /* XXX TBD */ "Access denied - ACL LUN conflict") }, /* T */ { SST(0x20, 0x0C, SS_FATAL | EINVAL, "Illegal command when not in append-only mode") }, /* DT WRO BK */ { SST(0x21, 0x00, SS_FATAL | EINVAL, "Logical block address out of range") }, /* DT WROM BK */ { SST(0x21, 0x01, SS_FATAL | EINVAL, "Invalid element address") }, /* R */ { SST(0x21, 0x02, SS_RDEF, /* XXX TBD */ "Invalid address for write") }, /* R */ { SST(0x21, 0x03, SS_RDEF, /* XXX TBD */ "Invalid write crossing layer jump") }, /* D */ { SST(0x21, 0x04, SS_RDEF, /* XXX TBD */ "Unaligned write command") }, /* D */ { SST(0x21, 0x05, SS_RDEF, /* XXX TBD */ "Write boundary violation") }, /* D */ { SST(0x21, 0x06, SS_RDEF, /* XXX TBD */ "Attempt to read invalid data") }, /* D */ { SST(0x21, 0x07, SS_RDEF, /* XXX TBD */ "Read boundary violation") }, /* D */ { SST(0x22, 0x00, SS_FATAL | EINVAL, "Illegal function (use 20 00, 24 00, or 26 00)") }, /* DT P B */ { SST(0x23, 0x00, SS_FATAL | EINVAL, "Invalid token operation, cause not reportable") }, /* DT P B */ { SST(0x23, 0x01, SS_FATAL | EINVAL, "Invalid token operation, unsupported token type") }, /* DT P B */ { SST(0x23, 0x02, SS_FATAL | EINVAL, "Invalid token operation, remote token usage not supported") }, /* DT P B */ { SST(0x23, 0x03, SS_FATAL | EINVAL, "Invalid token operation, remote ROD token creation not supported") }, /* DT P B */ { SST(0x23, 0x04, SS_FATAL | EINVAL, "Invalid token operation, token unknown") }, /* DT P B */ { SST(0x23, 0x05, SS_FATAL | EINVAL, "Invalid token operation, token corrupt") }, /* DT P B */ { SST(0x23, 0x06, SS_FATAL | EINVAL, "Invalid token operation, token revoked") }, /* DT P B */ { SST(0x23, 0x07, SS_FATAL | EINVAL, "Invalid token operation, token expired") }, /* DT P B */ { SST(0x23, 0x08, SS_FATAL | EINVAL, "Invalid token operation, token cancelled") }, /* DT P B */ { SST(0x23, 0x09, SS_FATAL | EINVAL, "Invalid token operation, token deleted") }, /* DT P B */ { SST(0x23, 0x0A, SS_FATAL | EINVAL, "Invalid token operation, invalid token length") }, /* DTLPWROMAEBKVF */ { SST(0x24, 0x00, SS_FATAL | EINVAL, "Invalid field in CDB") }, /* DTLPWRO AEBKVF */ { SST(0x24, 0x01, SS_RDEF, /* XXX TBD */ "CDB decryption error") }, /* T */ { SST(0x24, 0x02, SS_RDEF, /* XXX TBD */ "Obsolete") }, /* T */ { SST(0x24, 0x03, SS_RDEF, /* XXX TBD */ "Obsolete") }, /* F */ { SST(0x24, 0x04, SS_RDEF, /* XXX TBD */ "Security audit value frozen") }, /* F */ { SST(0x24, 0x05, SS_RDEF, /* XXX TBD */ "Security working key frozen") }, /* F */ { SST(0x24, 0x06, SS_RDEF, /* XXX TBD */ "NONCE not unique") }, /* F */ { SST(0x24, 0x07, SS_RDEF, /* XXX TBD */ "NONCE timestamp out of range") }, /* DT R MAEBKV */ { SST(0x24, 0x08, SS_RDEF, /* XXX TBD */ "Invalid XCDB") }, /* DTLPWROMAEBKVF */ { SST(0x25, 0x00, SS_FATAL | ENXIO | SSQ_LOST, "Logical unit not supported") }, /* DTLPWROMAEBKVF */ { SST(0x26, 0x00, SS_FATAL | EINVAL, "Invalid field in parameter list") }, /* DTLPWROMAEBKVF */ { SST(0x26, 0x01, SS_FATAL | EINVAL, "Parameter not supported") }, /* DTLPWROMAEBKVF */ { SST(0x26, 0x02, SS_FATAL | EINVAL, "Parameter value invalid") }, /* DTLPWROMAE K */ { SST(0x26, 0x03, SS_FATAL | EINVAL, "Threshold parameters not supported") }, /* DTLPWROMAEBKVF */ { SST(0x26, 0x04, SS_FATAL | EINVAL, "Invalid release of persistent reservation") }, /* DTLPWRO A BK */ { SST(0x26, 0x05, SS_RDEF, /* XXX TBD */ "Data decryption error") }, /* DTLPWRO K */ { SST(0x26, 0x06, SS_FATAL | EINVAL, "Too many target descriptors") }, /* DTLPWRO K */ { SST(0x26, 0x07, SS_FATAL | EINVAL, "Unsupported target descriptor type code") }, /* DTLPWRO K */ { SST(0x26, 0x08, SS_FATAL | EINVAL, "Too many segment descriptors") }, /* DTLPWRO K */ { SST(0x26, 0x09, SS_FATAL | EINVAL, "Unsupported segment descriptor type code") }, /* DTLPWRO K */ { SST(0x26, 0x0A, SS_FATAL | EINVAL, "Unexpected inexact segment") }, /* DTLPWRO K */ { SST(0x26, 0x0B, SS_FATAL | EINVAL, "Inline data length exceeded") }, /* DTLPWRO K */ { SST(0x26, 0x0C, SS_FATAL | EINVAL, "Invalid operation for copy source or destination") }, /* DTLPWRO K */ { SST(0x26, 0x0D, SS_FATAL | EINVAL, "Copy segment granularity violation") }, /* DT PWROMAEBK */ { SST(0x26, 0x0E, SS_RDEF, /* XXX TBD */ "Invalid parameter while port is enabled") }, /* F */ { SST(0x26, 0x0F, SS_RDEF, /* XXX TBD */ "Invalid data-out buffer integrity check value") }, /* T */ { SST(0x26, 0x10, SS_RDEF, /* XXX TBD */ "Data decryption key fail limit reached") }, /* T */ { SST(0x26, 0x11, SS_RDEF, /* XXX TBD */ "Incomplete key-associated data set") }, /* T */ { SST(0x26, 0x12, SS_RDEF, /* XXX TBD */ "Vendor specific key reference not found") }, /* D */ { SST(0x26, 0x13, SS_RDEF, /* XXX TBD */ "Application tag mode page is invalid") }, /* DT WRO BK */ { SST(0x27, 0x00, SS_FATAL | EACCES, "Write protected") }, /* DT WRO BK */ { SST(0x27, 0x01, SS_FATAL | EACCES, "Hardware write protected") }, /* DT WRO BK */ { SST(0x27, 0x02, SS_FATAL | EACCES, "Logical unit software write protected") }, /* T R */ { SST(0x27, 0x03, SS_FATAL | EACCES, "Associated write protect") }, /* T R */ { SST(0x27, 0x04, SS_FATAL | EACCES, "Persistent write protect") }, /* T R */ { SST(0x27, 0x05, SS_FATAL | EACCES, "Permanent write protect") }, /* R F */ { SST(0x27, 0x06, SS_RDEF, /* XXX TBD */ "Conditional write protect") }, /* D B */ { SST(0x27, 0x07, SS_FATAL | ENOSPC, "Space allocation failed write protect") }, /* D */ { SST(0x27, 0x08, SS_FATAL | EACCES, "Zone is read only") }, /* DTLPWROMAEBKVF */ { SST(0x28, 0x00, SS_FATAL | ENXIO, "Not ready to ready change, medium may have changed") }, /* DT WROM B */ { SST(0x28, 0x01, SS_FATAL | ENXIO, "Import or export element accessed") }, /* R */ { SST(0x28, 0x02, SS_RDEF, /* XXX TBD */ "Format-layer may have changed") }, /* M */ { SST(0x28, 0x03, SS_RDEF, /* XXX TBD */ "Import/export element accessed, medium changed") }, /* * XXX JGibbs - All of these should use the same errno, but I don't * think ENXIO is the correct choice. Should we borrow from * the networking errnos? ECONNRESET anyone? */ /* DTLPWROMAEBKVF */ { SST(0x29, 0x00, SS_FATAL | ENXIO, "Power on, reset, or bus device reset occurred") }, /* DTLPWROMAEBKVF */ { SST(0x29, 0x01, SS_RDEF, "Power on occurred") }, /* DTLPWROMAEBKVF */ { SST(0x29, 0x02, SS_RDEF, "SCSI bus reset occurred") }, /* DTLPWROMAEBKVF */ { SST(0x29, 0x03, SS_RDEF, "Bus device reset function occurred") }, /* DTLPWROMAEBKVF */ { SST(0x29, 0x04, SS_RDEF, "Device internal reset") }, /* DTLPWROMAEBKVF */ { SST(0x29, 0x05, SS_RDEF, "Transceiver mode changed to single-ended") }, /* DTLPWROMAEBKVF */ { SST(0x29, 0x06, SS_RDEF, "Transceiver mode changed to LVD") }, /* DTLPWROMAEBKVF */ { SST(0x29, 0x07, SS_RDEF, /* XXX TBD */ "I_T nexus loss occurred") }, /* DTL WROMAEBKVF */ { SST(0x2A, 0x00, SS_RDEF, "Parameters changed") }, /* DTL WROMAEBKVF */ { SST(0x2A, 0x01, SS_RDEF, "Mode parameters changed") }, /* DTL WROMAE K */ { SST(0x2A, 0x02, SS_RDEF, "Log parameters changed") }, /* DTLPWROMAE K */ { SST(0x2A, 0x03, SS_RDEF, "Reservations preempted") }, /* DTLPWROMAE */ { SST(0x2A, 0x04, SS_RDEF, /* XXX TBD */ "Reservations released") }, /* DTLPWROMAE */ { SST(0x2A, 0x05, SS_RDEF, /* XXX TBD */ "Registrations preempted") }, /* DTLPWROMAEBKVF */ { SST(0x2A, 0x06, SS_RDEF, /* XXX TBD */ "Asymmetric access state changed") }, /* DTLPWROMAEBKVF */ { SST(0x2A, 0x07, SS_RDEF, /* XXX TBD */ "Implicit asymmetric access state transition failed") }, /* DT WROMAEBKVF */ { SST(0x2A, 0x08, SS_RDEF, /* XXX TBD */ "Priority changed") }, /* D */ { SST(0x2A, 0x09, SS_RDEF, /* XXX TBD */ "Capacity data has changed") }, /* DT */ { SST(0x2A, 0x0A, SS_RDEF, /* XXX TBD */ "Error history I_T nexus cleared") }, /* DT */ { SST(0x2A, 0x0B, SS_RDEF, /* XXX TBD */ "Error history snapshot released") }, /* F */ { SST(0x2A, 0x0C, SS_RDEF, /* XXX TBD */ "Error recovery attributes have changed") }, /* T */ { SST(0x2A, 0x0D, SS_RDEF, /* XXX TBD */ "Data encryption capabilities changed") }, /* DT M E V */ { SST(0x2A, 0x10, SS_RDEF, /* XXX TBD */ "Timestamp changed") }, /* T */ { SST(0x2A, 0x11, SS_RDEF, /* XXX TBD */ "Data encryption parameters changed by another I_T nexus") }, /* T */ { SST(0x2A, 0x12, SS_RDEF, /* XXX TBD */ "Data encryption parameters changed by vendor specific event") }, /* T */ { SST(0x2A, 0x13, SS_RDEF, /* XXX TBD */ "Data encryption key instance counter has changed") }, /* DT R MAEBKV */ { SST(0x2A, 0x14, SS_RDEF, /* XXX TBD */ "SA creation capabilities data has changed") }, /* T M V */ { SST(0x2A, 0x15, SS_RDEF, /* XXX TBD */ "Medium removal prevention preempted") }, /* DTLPWRO K */ { SST(0x2B, 0x00, SS_RDEF, "Copy cannot execute since host cannot disconnect") }, /* DTLPWROMAEBKVF */ { SST(0x2C, 0x00, SS_RDEF, "Command sequence error") }, /* */ { SST(0x2C, 0x01, SS_RDEF, "Too many windows specified") }, /* */ { SST(0x2C, 0x02, SS_RDEF, "Invalid combination of windows specified") }, /* R */ { SST(0x2C, 0x03, SS_RDEF, "Current program area is not empty") }, /* R */ { SST(0x2C, 0x04, SS_RDEF, "Current program area is empty") }, /* B */ { SST(0x2C, 0x05, SS_RDEF, /* XXX TBD */ "Illegal power condition request") }, /* R */ { SST(0x2C, 0x06, SS_RDEF, /* XXX TBD */ "Persistent prevent conflict") }, /* DTLPWROMAEBKVF */ { SST(0x2C, 0x07, SS_RDEF, /* XXX TBD */ "Previous busy status") }, /* DTLPWROMAEBKVF */ { SST(0x2C, 0x08, SS_RDEF, /* XXX TBD */ "Previous task set full status") }, /* DTLPWROM EBKVF */ { SST(0x2C, 0x09, SS_RDEF, /* XXX TBD */ "Previous reservation conflict status") }, /* F */ { SST(0x2C, 0x0A, SS_RDEF, /* XXX TBD */ "Partition or collection contains user objects") }, /* T */ { SST(0x2C, 0x0B, SS_RDEF, /* XXX TBD */ "Not reserved") }, /* D */ { SST(0x2C, 0x0C, SS_RDEF, /* XXX TBD */ "ORWRITE generation does not match") }, /* D */ { SST(0x2C, 0x0D, SS_RDEF, /* XXX TBD */ "Reset write pointer not allowed") }, /* D */ { SST(0x2C, 0x0E, SS_RDEF, /* XXX TBD */ "Zone is offline") }, /* D */ { SST(0x2C, 0x0F, SS_RDEF, /* XXX TBD */ "Stream not open") }, /* D */ { SST(0x2C, 0x10, SS_RDEF, /* XXX TBD */ "Unwritten data in zone") }, /* T */ { SST(0x2D, 0x00, SS_RDEF, "Overwrite error on update in place") }, /* R */ { SST(0x2E, 0x00, SS_RDEF, /* XXX TBD */ "Insufficient time for operation") }, /* D */ { SST(0x2E, 0x01, SS_RDEF, /* XXX TBD */ "Command timeout before processing") }, /* D */ { SST(0x2E, 0x02, SS_RDEF, /* XXX TBD */ "Command timeout during processing") }, /* D */ { SST(0x2E, 0x03, SS_RDEF, /* XXX TBD */ "Command timeout during processing due to error recovery") }, /* DTLPWROMAEBKVF */ { SST(0x2F, 0x00, SS_RDEF, "Commands cleared by another initiator") }, /* D */ { SST(0x2F, 0x01, SS_RDEF, /* XXX TBD */ "Commands cleared by power loss notification") }, /* DTLPWROMAEBKVF */ { SST(0x2F, 0x02, SS_RDEF, /* XXX TBD */ "Commands cleared by device server") }, /* DTLPWROMAEBKVF */ { SST(0x2F, 0x03, SS_RDEF, /* XXX TBD */ "Some commands cleared by queuing layer event") }, /* DT WROM BK */ { SST(0x30, 0x00, SS_RDEF, "Incompatible medium installed") }, /* DT WRO BK */ { SST(0x30, 0x01, SS_RDEF, "Cannot read medium - unknown format") }, /* DT WRO BK */ { SST(0x30, 0x02, SS_RDEF, "Cannot read medium - incompatible format") }, /* DT R K */ { SST(0x30, 0x03, SS_RDEF, "Cleaning cartridge installed") }, /* DT WRO BK */ { SST(0x30, 0x04, SS_RDEF, "Cannot write medium - unknown format") }, /* DT WRO BK */ { SST(0x30, 0x05, SS_RDEF, "Cannot write medium - incompatible format") }, /* DT WRO B */ { SST(0x30, 0x06, SS_RDEF, "Cannot format medium - incompatible medium") }, /* DTL WROMAEBKVF */ { SST(0x30, 0x07, SS_RDEF, "Cleaning failure") }, /* R */ { SST(0x30, 0x08, SS_RDEF, "Cannot write - application code mismatch") }, /* R */ { SST(0x30, 0x09, SS_RDEF, "Current session not fixated for append") }, /* DT WRO AEBK */ { SST(0x30, 0x0A, SS_RDEF, /* XXX TBD */ "Cleaning request rejected") }, /* T */ { SST(0x30, 0x0C, SS_RDEF, /* XXX TBD */ "WORM medium - overwrite attempted") }, /* T */ { SST(0x30, 0x0D, SS_RDEF, /* XXX TBD */ "WORM medium - integrity check") }, /* R */ { SST(0x30, 0x10, SS_RDEF, /* XXX TBD */ "Medium not formatted") }, /* M */ { SST(0x30, 0x11, SS_RDEF, /* XXX TBD */ "Incompatible volume type") }, /* M */ { SST(0x30, 0x12, SS_RDEF, /* XXX TBD */ "Incompatible volume qualifier") }, /* M */ { SST(0x30, 0x13, SS_RDEF, /* XXX TBD */ "Cleaning volume expired") }, /* DT WRO BK */ { SST(0x31, 0x00, SS_RDEF, "Medium format corrupted") }, /* D L RO B */ { SST(0x31, 0x01, SS_RDEF, "Format command failed") }, /* R */ { SST(0x31, 0x02, SS_RDEF, /* XXX TBD */ "Zoned formatting failed due to spare linking") }, /* D B */ { SST(0x31, 0x03, SS_RDEF, /* XXX TBD */ "SANITIZE command failed") }, /* D W O BK */ { SST(0x32, 0x00, SS_RDEF, "No defect spare location available") }, /* D W O BK */ { SST(0x32, 0x01, SS_RDEF, "Defect list update failure") }, /* T */ { SST(0x33, 0x00, SS_RDEF, "Tape length error") }, /* DTLPWROMAEBKVF */ { SST(0x34, 0x00, SS_RDEF, "Enclosure failure") }, /* DTLPWROMAEBKVF */ { SST(0x35, 0x00, SS_RDEF, "Enclosure services failure") }, /* DTLPWROMAEBKVF */ { SST(0x35, 0x01, SS_RDEF, "Unsupported enclosure function") }, /* DTLPWROMAEBKVF */ { SST(0x35, 0x02, SS_RDEF, "Enclosure services unavailable") }, /* DTLPWROMAEBKVF */ { SST(0x35, 0x03, SS_RDEF, "Enclosure services transfer failure") }, /* DTLPWROMAEBKVF */ { SST(0x35, 0x04, SS_RDEF, "Enclosure services transfer refused") }, /* DTL WROMAEBKVF */ { SST(0x35, 0x05, SS_RDEF, /* XXX TBD */ "Enclosure services checksum error") }, /* L */ { SST(0x36, 0x00, SS_RDEF, "Ribbon, ink, or toner failure") }, /* DTL WROMAEBKVF */ { SST(0x37, 0x00, SS_RDEF, "Rounded parameter") }, /* B */ { SST(0x38, 0x00, SS_RDEF, /* XXX TBD */ "Event status notification") }, /* B */ { SST(0x38, 0x02, SS_RDEF, /* XXX TBD */ "ESN - power management class event") }, /* B */ { SST(0x38, 0x04, SS_RDEF, /* XXX TBD */ "ESN - media class event") }, /* B */ { SST(0x38, 0x06, SS_RDEF, /* XXX TBD */ "ESN - device busy class event") }, /* D */ { SST(0x38, 0x07, SS_RDEF, /* XXX TBD */ "Thin provisioning soft threshold reached") }, /* DTL WROMAE K */ { SST(0x39, 0x00, SS_RDEF, "Saving parameters not supported") }, /* DTL WROM BK */ { SST(0x3A, 0x00, SS_FATAL | ENXIO, "Medium not present") }, /* DT WROM BK */ { SST(0x3A, 0x01, SS_FATAL | ENXIO, "Medium not present - tray closed") }, /* DT WROM BK */ { SST(0x3A, 0x02, SS_FATAL | ENXIO, "Medium not present - tray open") }, /* DT WROM B */ { SST(0x3A, 0x03, SS_RDEF, /* XXX TBD */ "Medium not present - loadable") }, /* DT WRO B */ { SST(0x3A, 0x04, SS_RDEF, /* XXX TBD */ "Medium not present - medium auxiliary memory accessible") }, /* TL */ { SST(0x3B, 0x00, SS_RDEF, "Sequential positioning error") }, /* T */ { SST(0x3B, 0x01, SS_RDEF, "Tape position error at beginning-of-medium") }, /* T */ { SST(0x3B, 0x02, SS_RDEF, "Tape position error at end-of-medium") }, /* L */ { SST(0x3B, 0x03, SS_RDEF, "Tape or electronic vertical forms unit not ready") }, /* L */ { SST(0x3B, 0x04, SS_RDEF, "Slew failure") }, /* L */ { SST(0x3B, 0x05, SS_RDEF, "Paper jam") }, /* L */ { SST(0x3B, 0x06, SS_RDEF, "Failed to sense top-of-form") }, /* L */ { SST(0x3B, 0x07, SS_RDEF, "Failed to sense bottom-of-form") }, /* T */ { SST(0x3B, 0x08, SS_RDEF, "Reposition error") }, /* */ { SST(0x3B, 0x09, SS_RDEF, "Read past end of medium") }, /* */ { SST(0x3B, 0x0A, SS_RDEF, "Read past beginning of medium") }, /* */ { SST(0x3B, 0x0B, SS_RDEF, "Position past end of medium") }, /* T */ { SST(0x3B, 0x0C, SS_RDEF, "Position past beginning of medium") }, /* DT WROM BK */ { SST(0x3B, 0x0D, SS_FATAL | ENOSPC, "Medium destination element full") }, /* DT WROM BK */ { SST(0x3B, 0x0E, SS_RDEF, "Medium source element empty") }, /* R */ { SST(0x3B, 0x0F, SS_RDEF, "End of medium reached") }, /* DT WROM BK */ { SST(0x3B, 0x11, SS_RDEF, "Medium magazine not accessible") }, /* DT WROM BK */ { SST(0x3B, 0x12, SS_RDEF, "Medium magazine removed") }, /* DT WROM BK */ { SST(0x3B, 0x13, SS_RDEF, "Medium magazine inserted") }, /* DT WROM BK */ { SST(0x3B, 0x14, SS_RDEF, "Medium magazine locked") }, /* DT WROM BK */ { SST(0x3B, 0x15, SS_RDEF, "Medium magazine unlocked") }, /* R */ { SST(0x3B, 0x16, SS_RDEF, /* XXX TBD */ "Mechanical positioning or changer error") }, /* F */ { SST(0x3B, 0x17, SS_RDEF, /* XXX TBD */ "Read past end of user object") }, /* M */ { SST(0x3B, 0x18, SS_RDEF, /* XXX TBD */ "Element disabled") }, /* M */ { SST(0x3B, 0x19, SS_RDEF, /* XXX TBD */ "Element enabled") }, /* M */ { SST(0x3B, 0x1A, SS_RDEF, /* XXX TBD */ "Data transfer device removed") }, /* M */ { SST(0x3B, 0x1B, SS_RDEF, /* XXX TBD */ "Data transfer device inserted") }, /* T */ { SST(0x3B, 0x1C, SS_RDEF, /* XXX TBD */ "Too many logical objects on partition to support operation") }, /* DTLPWROMAE K */ { SST(0x3D, 0x00, SS_RDEF, "Invalid bits in IDENTIFY message") }, /* DTLPWROMAEBKVF */ { SST(0x3E, 0x00, SS_RDEF, "Logical unit has not self-configured yet") }, /* DTLPWROMAEBKVF */ { SST(0x3E, 0x01, SS_RDEF, "Logical unit failure") }, /* DTLPWROMAEBKVF */ { SST(0x3E, 0x02, SS_RDEF, "Timeout on logical unit") }, /* DTLPWROMAEBKVF */ { SST(0x3E, 0x03, SS_RDEF, /* XXX TBD */ "Logical unit failed self-test") }, /* DTLPWROMAEBKVF */ { SST(0x3E, 0x04, SS_RDEF, /* XXX TBD */ "Logical unit unable to update self-test log") }, /* DTLPWROMAEBKVF */ { SST(0x3F, 0x00, SS_RDEF, "Target operating conditions have changed") }, /* DTLPWROMAEBKVF */ { SST(0x3F, 0x01, SS_RDEF, "Microcode has been changed") }, /* DTLPWROM BK */ { SST(0x3F, 0x02, SS_RDEF, "Changed operating definition") }, /* DTLPWROMAEBKVF */ { SST(0x3F, 0x03, SS_RDEF, "INQUIRY data has changed") }, /* DT WROMAEBK */ { SST(0x3F, 0x04, SS_RDEF, "Component device attached") }, /* DT WROMAEBK */ { SST(0x3F, 0x05, SS_RDEF, "Device identifier changed") }, /* DT WROMAEB */ { SST(0x3F, 0x06, SS_RDEF, "Redundancy group created or modified") }, /* DT WROMAEB */ { SST(0x3F, 0x07, SS_RDEF, "Redundancy group deleted") }, /* DT WROMAEB */ { SST(0x3F, 0x08, SS_RDEF, "Spare created or modified") }, /* DT WROMAEB */ { SST(0x3F, 0x09, SS_RDEF, "Spare deleted") }, /* DT WROMAEBK */ { SST(0x3F, 0x0A, SS_RDEF, "Volume set created or modified") }, /* DT WROMAEBK */ { SST(0x3F, 0x0B, SS_RDEF, "Volume set deleted") }, /* DT WROMAEBK */ { SST(0x3F, 0x0C, SS_RDEF, "Volume set deassigned") }, /* DT WROMAEBK */ { SST(0x3F, 0x0D, SS_RDEF, "Volume set reassigned") }, /* DTLPWROMAE */ { SST(0x3F, 0x0E, SS_RDEF | SSQ_RESCAN , "Reported LUNs data has changed") }, /* DTLPWROMAEBKVF */ { SST(0x3F, 0x0F, SS_RDEF, /* XXX TBD */ "Echo buffer overwritten") }, /* DT WROM B */ { SST(0x3F, 0x10, SS_RDEF, /* XXX TBD */ "Medium loadable") }, /* DT WROM B */ { SST(0x3F, 0x11, SS_RDEF, /* XXX TBD */ "Medium auxiliary memory accessible") }, /* DTLPWR MAEBK F */ { SST(0x3F, 0x12, SS_RDEF, /* XXX TBD */ "iSCSI IP address added") }, /* DTLPWR MAEBK F */ { SST(0x3F, 0x13, SS_RDEF, /* XXX TBD */ "iSCSI IP address removed") }, /* DTLPWR MAEBK F */ { SST(0x3F, 0x14, SS_RDEF, /* XXX TBD */ "iSCSI IP address changed") }, /* DTLPWR MAEBK */ { SST(0x3F, 0x15, SS_RDEF, /* XXX TBD */ "Inspect referrals sense descriptors") }, /* DTLPWROMAEBKVF */ { SST(0x3F, 0x16, SS_RDEF, /* XXX TBD */ "Microcode has been changed without reset") }, /* D */ { SST(0x3F, 0x17, SS_RDEF, /* XXX TBD */ "Zone transition to full") }, /* D */ { SST(0x40, 0x00, SS_RDEF, "RAM failure") }, /* deprecated - use 40 NN instead */ /* DTLPWROMAEBKVF */ { SST(0x40, 0x80, SS_RDEF, "Diagnostic failure: ASCQ = Component ID") }, /* DTLPWROMAEBKVF */ { SST(0x40, 0xFF, SS_RDEF | SSQ_RANGE, NULL) }, /* Range 0x80->0xFF */ /* D */ { SST(0x41, 0x00, SS_RDEF, "Data path failure") }, /* deprecated - use 40 NN instead */ /* D */ { SST(0x42, 0x00, SS_RDEF, "Power-on or self-test failure") }, /* deprecated - use 40 NN instead */ /* DTLPWROMAEBKVF */ { SST(0x43, 0x00, SS_RDEF, "Message error") }, /* DTLPWROMAEBKVF */ { SST(0x44, 0x00, SS_FATAL | EIO, "Internal target failure") }, /* DT P MAEBKVF */ { SST(0x44, 0x01, SS_RDEF, /* XXX TBD */ "Persistent reservation information lost") }, /* DT B */ { SST(0x44, 0x71, SS_RDEF, /* XXX TBD */ "ATA device failed set features") }, /* DTLPWROMAEBKVF */ { SST(0x45, 0x00, SS_RDEF, "Select or reselect failure") }, /* DTLPWROM BK */ { SST(0x46, 0x00, SS_RDEF, "Unsuccessful soft reset") }, /* DTLPWROMAEBKVF */ { SST(0x47, 0x00, SS_RDEF, "SCSI parity error") }, /* DTLPWROMAEBKVF */ { SST(0x47, 0x01, SS_RDEF, /* XXX TBD */ "Data phase CRC error detected") }, /* DTLPWROMAEBKVF */ { SST(0x47, 0x02, SS_RDEF, /* XXX TBD */ "SCSI parity error detected during ST data phase") }, /* DTLPWROMAEBKVF */ { SST(0x47, 0x03, SS_RDEF, /* XXX TBD */ "Information unit iuCRC error detected") }, /* DTLPWROMAEBKVF */ { SST(0x47, 0x04, SS_RDEF, /* XXX TBD */ "Asynchronous information protection error detected") }, /* DTLPWROMAEBKVF */ { SST(0x47, 0x05, SS_RDEF, /* XXX TBD */ "Protocol service CRC error") }, /* DT MAEBKVF */ { SST(0x47, 0x06, SS_RDEF, /* XXX TBD */ "PHY test function in progress") }, /* DT PWROMAEBK */ { SST(0x47, 0x7F, SS_RDEF, /* XXX TBD */ "Some commands cleared by iSCSI protocol event") }, /* DTLPWROMAEBKVF */ { SST(0x48, 0x00, SS_RDEF, "Initiator detected error message received") }, /* DTLPWROMAEBKVF */ { SST(0x49, 0x00, SS_RDEF, "Invalid message error") }, /* DTLPWROMAEBKVF */ { SST(0x4A, 0x00, SS_RDEF, "Command phase error") }, /* DTLPWROMAEBKVF */ { SST(0x4B, 0x00, SS_RDEF, "Data phase error") }, /* DT PWROMAEBK */ { SST(0x4B, 0x01, SS_RDEF, /* XXX TBD */ "Invalid target port transfer tag received") }, /* DT PWROMAEBK */ { SST(0x4B, 0x02, SS_RDEF, /* XXX TBD */ "Too much write data") }, /* DT PWROMAEBK */ { SST(0x4B, 0x03, SS_RDEF, /* XXX TBD */ "ACK/NAK timeout") }, /* DT PWROMAEBK */ { SST(0x4B, 0x04, SS_RDEF, /* XXX TBD */ "NAK received") }, /* DT PWROMAEBK */ { SST(0x4B, 0x05, SS_RDEF, /* XXX TBD */ "Data offset error") }, /* DT PWROMAEBK */ { SST(0x4B, 0x06, SS_RDEF, /* XXX TBD */ "Initiator response timeout") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x07, SS_RDEF, /* XXX TBD */ "Connection lost") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x08, SS_RDEF, /* XXX TBD */ "Data-in buffer overflow - data buffer size") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x09, SS_RDEF, /* XXX TBD */ "Data-in buffer overflow - data buffer descriptor area") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x0A, SS_RDEF, /* XXX TBD */ "Data-in buffer error") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x0B, SS_RDEF, /* XXX TBD */ "Data-out buffer overflow - data buffer size") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x0C, SS_RDEF, /* XXX TBD */ "Data-out buffer overflow - data buffer descriptor area") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x0D, SS_RDEF, /* XXX TBD */ "Data-out buffer error") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x0E, SS_RDEF, /* XXX TBD */ "PCIe fabric error") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x0F, SS_RDEF, /* XXX TBD */ "PCIe completion timeout") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x10, SS_RDEF, /* XXX TBD */ "PCIe completer abort") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x11, SS_RDEF, /* XXX TBD */ "PCIe poisoned TLP received") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x12, SS_RDEF, /* XXX TBD */ "PCIe ECRC check failed") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x13, SS_RDEF, /* XXX TBD */ "PCIe unsupported request") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x14, SS_RDEF, /* XXX TBD */ "PCIe ACS violation") }, /* DT PWROMAEBK F */ { SST(0x4B, 0x15, SS_RDEF, /* XXX TBD */ "PCIe TLP prefix blocket") }, /* DTLPWROMAEBKVF */ { SST(0x4C, 0x00, SS_RDEF, "Logical unit failed self-configuration") }, /* DTLPWROMAEBKVF */ { SST(0x4D, 0x00, SS_RDEF, "Tagged overlapped commands: ASCQ = Queue tag ID") }, /* DTLPWROMAEBKVF */ { SST(0x4D, 0xFF, SS_RDEF | SSQ_RANGE, NULL) }, /* Range 0x00->0xFF */ /* DTLPWROMAEBKVF */ { SST(0x4E, 0x00, SS_RDEF, "Overlapped commands attempted") }, /* T */ { SST(0x50, 0x00, SS_RDEF, "Write append error") }, /* T */ { SST(0x50, 0x01, SS_RDEF, "Write append position error") }, /* T */ { SST(0x50, 0x02, SS_RDEF, "Position error related to timing") }, /* T RO */ { SST(0x51, 0x00, SS_RDEF, "Erase failure") }, /* R */ { SST(0x51, 0x01, SS_RDEF, /* XXX TBD */ "Erase failure - incomplete erase operation detected") }, /* T */ { SST(0x52, 0x00, SS_RDEF, "Cartridge fault") }, /* DTL WROM BK */ { SST(0x53, 0x00, SS_RDEF, "Media load or eject failed") }, /* T */ { SST(0x53, 0x01, SS_RDEF, "Unload tape failure") }, /* DT WROM BK */ { SST(0x53, 0x02, SS_RDEF, "Medium removal prevented") }, /* M */ { SST(0x53, 0x03, SS_RDEF, /* XXX TBD */ "Medium removal prevented by data transfer element") }, /* T */ { SST(0x53, 0x04, SS_RDEF, /* XXX TBD */ "Medium thread or unthread failure") }, /* M */ { SST(0x53, 0x05, SS_RDEF, /* XXX TBD */ "Volume identifier invalid") }, /* T */ { SST(0x53, 0x06, SS_RDEF, /* XXX TBD */ "Volume identifier missing") }, /* M */ { SST(0x53, 0x07, SS_RDEF, /* XXX TBD */ "Duplicate volume identifier") }, /* M */ { SST(0x53, 0x08, SS_RDEF, /* XXX TBD */ "Element status unknown") }, /* M */ { SST(0x53, 0x09, SS_RDEF, /* XXX TBD */ "Data transfer device error - load failed") }, /* M */ { SST(0x53, 0x0A, SS_RDEF, /* XXX TBD */ "Data transfer device error - unload failed") }, /* M */ { SST(0x53, 0x0B, SS_RDEF, /* XXX TBD */ "Data transfer device error - unload missing") }, /* M */ { SST(0x53, 0x0C, SS_RDEF, /* XXX TBD */ "Data transfer device error - eject failed") }, /* M */ { SST(0x53, 0x0D, SS_RDEF, /* XXX TBD */ "Data transfer device error - library communication failed") }, /* P */ { SST(0x54, 0x00, SS_RDEF, "SCSI to host system interface failure") }, /* P */ { SST(0x55, 0x00, SS_RDEF, "System resource failure") }, /* D O BK */ { SST(0x55, 0x01, SS_FATAL | ENOSPC, "System buffer full") }, /* DTLPWROMAE K */ { SST(0x55, 0x02, SS_RDEF, /* XXX TBD */ "Insufficient reservation resources") }, /* DTLPWROMAE K */ { SST(0x55, 0x03, SS_RDEF, /* XXX TBD */ "Insufficient resources") }, /* DTLPWROMAE K */ { SST(0x55, 0x04, SS_RDEF, /* XXX TBD */ "Insufficient registration resources") }, /* DT PWROMAEBK */ { SST(0x55, 0x05, SS_RDEF, /* XXX TBD */ "Insufficient access control resources") }, /* DT WROM B */ { SST(0x55, 0x06, SS_RDEF, /* XXX TBD */ "Auxiliary memory out of space") }, /* F */ { SST(0x55, 0x07, SS_RDEF, /* XXX TBD */ "Quota error") }, /* T */ { SST(0x55, 0x08, SS_RDEF, /* XXX TBD */ "Maximum number of supplemental decryption keys exceeded") }, /* M */ { SST(0x55, 0x09, SS_RDEF, /* XXX TBD */ "Medium auxiliary memory not accessible") }, /* M */ { SST(0x55, 0x0A, SS_RDEF, /* XXX TBD */ "Data currently unavailable") }, /* DTLPWROMAEBKVF */ { SST(0x55, 0x0B, SS_RDEF, /* XXX TBD */ "Insufficient power for operation") }, /* DT P B */ { SST(0x55, 0x0C, SS_RDEF, /* XXX TBD */ "Insufficient resources to create ROD") }, /* DT P B */ { SST(0x55, 0x0D, SS_RDEF, /* XXX TBD */ "Insufficient resources to create ROD token") }, /* D */ { SST(0x55, 0x0E, SS_RDEF, /* XXX TBD */ "Insufficient zone resources") }, /* D */ { SST(0x55, 0x0F, SS_RDEF, /* XXX TBD */ "Insufficient zone resources to complete write") }, /* D */ { SST(0x55, 0x10, SS_RDEF, /* XXX TBD */ "Maximum number of streams open") }, /* R */ { SST(0x57, 0x00, SS_RDEF, "Unable to recover table-of-contents") }, /* O */ { SST(0x58, 0x00, SS_RDEF, "Generation does not exist") }, /* O */ { SST(0x59, 0x00, SS_RDEF, "Updated block read") }, /* DTLPWRO BK */ { SST(0x5A, 0x00, SS_RDEF, "Operator request or state change input") }, /* DT WROM BK */ { SST(0x5A, 0x01, SS_RDEF, "Operator medium removal request") }, /* DT WRO A BK */ { SST(0x5A, 0x02, SS_RDEF, "Operator selected write protect") }, /* DT WRO A BK */ { SST(0x5A, 0x03, SS_RDEF, "Operator selected write permit") }, /* DTLPWROM K */ { SST(0x5B, 0x00, SS_RDEF, "Log exception") }, /* DTLPWROM K */ { SST(0x5B, 0x01, SS_RDEF, "Threshold condition met") }, /* DTLPWROM K */ { SST(0x5B, 0x02, SS_RDEF, "Log counter at maximum") }, /* DTLPWROM K */ { SST(0x5B, 0x03, SS_RDEF, "Log list codes exhausted") }, /* D O */ { SST(0x5C, 0x00, SS_RDEF, "RPL status change") }, /* D O */ { SST(0x5C, 0x01, SS_NOP | SSQ_PRINT_SENSE, "Spindles synchronized") }, /* D O */ { SST(0x5C, 0x02, SS_RDEF, "Spindles not synchronized") }, /* DTLPWROMAEBKVF */ { SST(0x5D, 0x00, SS_NOP | SSQ_PRINT_SENSE, "Failure prediction threshold exceeded") }, /* R B */ { SST(0x5D, 0x01, SS_NOP | SSQ_PRINT_SENSE, "Media failure prediction threshold exceeded") }, /* R */ { SST(0x5D, 0x02, SS_NOP | SSQ_PRINT_SENSE, "Logical unit failure prediction threshold exceeded") }, /* R */ { SST(0x5D, 0x03, SS_NOP | SSQ_PRINT_SENSE, "Spare area exhaustion prediction threshold exceeded") }, /* D B */ { SST(0x5D, 0x10, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure general hard drive failure") }, /* D B */ { SST(0x5D, 0x11, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure drive error rate too high") }, /* D B */ { SST(0x5D, 0x12, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure data error rate too high") }, /* D B */ { SST(0x5D, 0x13, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure seek error rate too high") }, /* D B */ { SST(0x5D, 0x14, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure too many block reassigns") }, /* D B */ { SST(0x5D, 0x15, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure access times too high") }, /* D B */ { SST(0x5D, 0x16, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure start unit times too high") }, /* D B */ { SST(0x5D, 0x17, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure channel parametrics") }, /* D B */ { SST(0x5D, 0x18, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure controller detected") }, /* D B */ { SST(0x5D, 0x19, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure throughput performance") }, /* D B */ { SST(0x5D, 0x1A, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure seek time performance") }, /* D B */ { SST(0x5D, 0x1B, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure spin-up retry count") }, /* D B */ { SST(0x5D, 0x1C, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure drive calibration retry count") }, /* D B */ { SST(0x5D, 0x1D, SS_NOP | SSQ_PRINT_SENSE, "Hardware impending failure power loss protection circuit") }, /* D B */ { SST(0x5D, 0x20, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure general hard drive failure") }, /* D B */ { SST(0x5D, 0x21, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure drive error rate too high") }, /* D B */ { SST(0x5D, 0x22, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure data error rate too high") }, /* D B */ { SST(0x5D, 0x23, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure seek error rate too high") }, /* D B */ { SST(0x5D, 0x24, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure too many block reassigns") }, /* D B */ { SST(0x5D, 0x25, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure access times too high") }, /* D B */ { SST(0x5D, 0x26, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure start unit times too high") }, /* D B */ { SST(0x5D, 0x27, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure channel parametrics") }, /* D B */ { SST(0x5D, 0x28, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure controller detected") }, /* D B */ { SST(0x5D, 0x29, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure throughput performance") }, /* D B */ { SST(0x5D, 0x2A, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure seek time performance") }, /* D B */ { SST(0x5D, 0x2B, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure spin-up retry count") }, /* D B */ { SST(0x5D, 0x2C, SS_NOP | SSQ_PRINT_SENSE, "Controller impending failure drive calibration retry count") }, /* D B */ { SST(0x5D, 0x30, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure general hard drive failure") }, /* D B */ { SST(0x5D, 0x31, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure drive error rate too high") }, /* D B */ { SST(0x5D, 0x32, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure data error rate too high") }, /* D B */ { SST(0x5D, 0x33, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure seek error rate too high") }, /* D B */ { SST(0x5D, 0x34, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure too many block reassigns") }, /* D B */ { SST(0x5D, 0x35, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure access times too high") }, /* D B */ { SST(0x5D, 0x36, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure start unit times too high") }, /* D B */ { SST(0x5D, 0x37, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure channel parametrics") }, /* D B */ { SST(0x5D, 0x38, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure controller detected") }, /* D B */ { SST(0x5D, 0x39, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure throughput performance") }, /* D B */ { SST(0x5D, 0x3A, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure seek time performance") }, /* D B */ { SST(0x5D, 0x3B, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure spin-up retry count") }, /* D B */ { SST(0x5D, 0x3C, SS_NOP | SSQ_PRINT_SENSE, "Data channel impending failure drive calibration retry count") }, /* D B */ { SST(0x5D, 0x40, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure general hard drive failure") }, /* D B */ { SST(0x5D, 0x41, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure drive error rate too high") }, /* D B */ { SST(0x5D, 0x42, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure data error rate too high") }, /* D B */ { SST(0x5D, 0x43, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure seek error rate too high") }, /* D B */ { SST(0x5D, 0x44, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure too many block reassigns") }, /* D B */ { SST(0x5D, 0x45, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure access times too high") }, /* D B */ { SST(0x5D, 0x46, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure start unit times too high") }, /* D B */ { SST(0x5D, 0x47, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure channel parametrics") }, /* D B */ { SST(0x5D, 0x48, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure controller detected") }, /* D B */ { SST(0x5D, 0x49, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure throughput performance") }, /* D B */ { SST(0x5D, 0x4A, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure seek time performance") }, /* D B */ { SST(0x5D, 0x4B, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure spin-up retry count") }, /* D B */ { SST(0x5D, 0x4C, SS_NOP | SSQ_PRINT_SENSE, "Servo impending failure drive calibration retry count") }, /* D B */ { SST(0x5D, 0x50, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure general hard drive failure") }, /* D B */ { SST(0x5D, 0x51, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure drive error rate too high") }, /* D B */ { SST(0x5D, 0x52, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure data error rate too high") }, /* D B */ { SST(0x5D, 0x53, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure seek error rate too high") }, /* D B */ { SST(0x5D, 0x54, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure too many block reassigns") }, /* D B */ { SST(0x5D, 0x55, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure access times too high") }, /* D B */ { SST(0x5D, 0x56, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure start unit times too high") }, /* D B */ { SST(0x5D, 0x57, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure channel parametrics") }, /* D B */ { SST(0x5D, 0x58, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure controller detected") }, /* D B */ { SST(0x5D, 0x59, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure throughput performance") }, /* D B */ { SST(0x5D, 0x5A, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure seek time performance") }, /* D B */ { SST(0x5D, 0x5B, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure spin-up retry count") }, /* D B */ { SST(0x5D, 0x5C, SS_NOP | SSQ_PRINT_SENSE, "Spindle impending failure drive calibration retry count") }, /* D B */ { SST(0x5D, 0x60, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure general hard drive failure") }, /* D B */ { SST(0x5D, 0x61, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure drive error rate too high") }, /* D B */ { SST(0x5D, 0x62, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure data error rate too high") }, /* D B */ { SST(0x5D, 0x63, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure seek error rate too high") }, /* D B */ { SST(0x5D, 0x64, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure too many block reassigns") }, /* D B */ { SST(0x5D, 0x65, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure access times too high") }, /* D B */ { SST(0x5D, 0x66, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure start unit times too high") }, /* D B */ { SST(0x5D, 0x67, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure channel parametrics") }, /* D B */ { SST(0x5D, 0x68, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure controller detected") }, /* D B */ { SST(0x5D, 0x69, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure throughput performance") }, /* D B */ { SST(0x5D, 0x6A, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure seek time performance") }, /* D B */ { SST(0x5D, 0x6B, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure spin-up retry count") }, /* D B */ { SST(0x5D, 0x6C, SS_NOP | SSQ_PRINT_SENSE, "Firmware impending failure drive calibration retry count") }, /* D B */ { SST(0x5D, 0x73, SS_NOP | SSQ_PRINT_SENSE, "Media impending failure endurance limit met") }, /* DTLPWROMAEBKVF */ { SST(0x5D, 0xFF, SS_NOP | SSQ_PRINT_SENSE, "Failure prediction threshold exceeded (false)") }, /* DTLPWRO A K */ { SST(0x5E, 0x00, SS_RDEF, "Low power condition on") }, /* DTLPWRO A K */ { SST(0x5E, 0x01, SS_RDEF, "Idle condition activated by timer") }, /* DTLPWRO A K */ { SST(0x5E, 0x02, SS_RDEF, "Standby condition activated by timer") }, /* DTLPWRO A K */ { SST(0x5E, 0x03, SS_RDEF, "Idle condition activated by command") }, /* DTLPWRO A K */ { SST(0x5E, 0x04, SS_RDEF, "Standby condition activated by command") }, /* DTLPWRO A K */ { SST(0x5E, 0x05, SS_RDEF, "Idle-B condition activated by timer") }, /* DTLPWRO A K */ { SST(0x5E, 0x06, SS_RDEF, "Idle-B condition activated by command") }, /* DTLPWRO A K */ { SST(0x5E, 0x07, SS_RDEF, "Idle-C condition activated by timer") }, /* DTLPWRO A K */ { SST(0x5E, 0x08, SS_RDEF, "Idle-C condition activated by command") }, /* DTLPWRO A K */ { SST(0x5E, 0x09, SS_RDEF, "Standby-Y condition activated by timer") }, /* DTLPWRO A K */ { SST(0x5E, 0x0A, SS_RDEF, "Standby-Y condition activated by command") }, /* B */ { SST(0x5E, 0x41, SS_RDEF, /* XXX TBD */ "Power state change to active") }, /* B */ { SST(0x5E, 0x42, SS_RDEF, /* XXX TBD */ "Power state change to idle") }, /* B */ { SST(0x5E, 0x43, SS_RDEF, /* XXX TBD */ "Power state change to standby") }, /* B */ { SST(0x5E, 0x45, SS_RDEF, /* XXX TBD */ "Power state change to sleep") }, /* BK */ { SST(0x5E, 0x47, SS_RDEF, /* XXX TBD */ "Power state change to device control") }, /* */ { SST(0x60, 0x00, SS_RDEF, "Lamp failure") }, /* */ { SST(0x61, 0x00, SS_RDEF, "Video acquisition error") }, /* */ { SST(0x61, 0x01, SS_RDEF, "Unable to acquire video") }, /* */ { SST(0x61, 0x02, SS_RDEF, "Out of focus") }, /* */ { SST(0x62, 0x00, SS_RDEF, "Scan head positioning error") }, /* R */ { SST(0x63, 0x00, SS_RDEF, "End of user area encountered on this track") }, /* R */ { SST(0x63, 0x01, SS_FATAL | ENOSPC, "Packet does not fit in available space") }, /* R */ { SST(0x64, 0x00, SS_FATAL | ENXIO, "Illegal mode for this track") }, /* R */ { SST(0x64, 0x01, SS_RDEF, "Invalid packet size") }, /* DTLPWROMAEBKVF */ { SST(0x65, 0x00, SS_RDEF, "Voltage fault") }, /* */ { SST(0x66, 0x00, SS_RDEF, "Automatic document feeder cover up") }, /* */ { SST(0x66, 0x01, SS_RDEF, "Automatic document feeder lift up") }, /* */ { SST(0x66, 0x02, SS_RDEF, "Document jam in automatic document feeder") }, /* */ { SST(0x66, 0x03, SS_RDEF, "Document miss feed automatic in document feeder") }, /* A */ { SST(0x67, 0x00, SS_RDEF, "Configuration failure") }, /* A */ { SST(0x67, 0x01, SS_RDEF, "Configuration of incapable logical units failed") }, /* A */ { SST(0x67, 0x02, SS_RDEF, "Add logical unit failed") }, /* A */ { SST(0x67, 0x03, SS_RDEF, "Modification of logical unit failed") }, /* A */ { SST(0x67, 0x04, SS_RDEF, "Exchange of logical unit failed") }, /* A */ { SST(0x67, 0x05, SS_RDEF, "Remove of logical unit failed") }, /* A */ { SST(0x67, 0x06, SS_RDEF, "Attachment of logical unit failed") }, /* A */ { SST(0x67, 0x07, SS_RDEF, "Creation of logical unit failed") }, /* A */ { SST(0x67, 0x08, SS_RDEF, /* XXX TBD */ "Assign failure occurred") }, /* A */ { SST(0x67, 0x09, SS_RDEF, /* XXX TBD */ "Multiply assigned logical unit") }, /* DTLPWROMAEBKVF */ { SST(0x67, 0x0A, SS_RDEF, /* XXX TBD */ "Set target port groups command failed") }, /* DT B */ { SST(0x67, 0x0B, SS_RDEF, /* XXX TBD */ "ATA device feature not enabled") }, /* A */ { SST(0x68, 0x00, SS_RDEF, "Logical unit not configured") }, /* D */ { SST(0x68, 0x01, SS_RDEF, "Subsidiary logical unit not configured") }, /* A */ { SST(0x69, 0x00, SS_RDEF, "Data loss on logical unit") }, /* A */ { SST(0x69, 0x01, SS_RDEF, "Multiple logical unit failures") }, /* A */ { SST(0x69, 0x02, SS_RDEF, "Parity/data mismatch") }, /* A */ { SST(0x6A, 0x00, SS_RDEF, "Informational, refer to log") }, /* A */ { SST(0x6B, 0x00, SS_RDEF, "State change has occurred") }, /* A */ { SST(0x6B, 0x01, SS_RDEF, "Redundancy level got better") }, /* A */ { SST(0x6B, 0x02, SS_RDEF, "Redundancy level got worse") }, /* A */ { SST(0x6C, 0x00, SS_RDEF, "Rebuild failure occurred") }, /* A */ { SST(0x6D, 0x00, SS_RDEF, "Recalculate failure occurred") }, /* A */ { SST(0x6E, 0x00, SS_RDEF, "Command to logical unit failed") }, /* R */ { SST(0x6F, 0x00, SS_RDEF, /* XXX TBD */ "Copy protection key exchange failure - authentication failure") }, /* R */ { SST(0x6F, 0x01, SS_RDEF, /* XXX TBD */ "Copy protection key exchange failure - key not present") }, /* R */ { SST(0x6F, 0x02, SS_RDEF, /* XXX TBD */ "Copy protection key exchange failure - key not established") }, /* R */ { SST(0x6F, 0x03, SS_RDEF, /* XXX TBD */ "Read of scrambled sector without authentication") }, /* R */ { SST(0x6F, 0x04, SS_RDEF, /* XXX TBD */ "Media region code is mismatched to logical unit region") }, /* R */ { SST(0x6F, 0x05, SS_RDEF, /* XXX TBD */ "Drive region must be permanent/region reset count error") }, /* R */ { SST(0x6F, 0x06, SS_RDEF, /* XXX TBD */ "Insufficient block count for binding NONCE recording") }, /* R */ { SST(0x6F, 0x07, SS_RDEF, /* XXX TBD */ "Conflict in binding NONCE recording") }, /* T */ { SST(0x70, 0x00, SS_RDEF, "Decompression exception short: ASCQ = Algorithm ID") }, /* T */ { SST(0x70, 0xFF, SS_RDEF | SSQ_RANGE, NULL) }, /* Range 0x00 -> 0xFF */ /* T */ { SST(0x71, 0x00, SS_RDEF, "Decompression exception long: ASCQ = Algorithm ID") }, /* T */ { SST(0x71, 0xFF, SS_RDEF | SSQ_RANGE, NULL) }, /* Range 0x00 -> 0xFF */ /* R */ { SST(0x72, 0x00, SS_RDEF, "Session fixation error") }, /* R */ { SST(0x72, 0x01, SS_RDEF, "Session fixation error writing lead-in") }, /* R */ { SST(0x72, 0x02, SS_RDEF, "Session fixation error writing lead-out") }, /* R */ { SST(0x72, 0x03, SS_RDEF, "Session fixation error - incomplete track in session") }, /* R */ { SST(0x72, 0x04, SS_RDEF, "Empty or partially written reserved track") }, /* R */ { SST(0x72, 0x05, SS_RDEF, /* XXX TBD */ "No more track reservations allowed") }, /* R */ { SST(0x72, 0x06, SS_RDEF, /* XXX TBD */ "RMZ extension is not allowed") }, /* R */ { SST(0x72, 0x07, SS_RDEF, /* XXX TBD */ "No more test zone extensions are allowed") }, /* R */ { SST(0x73, 0x00, SS_RDEF, "CD control error") }, /* R */ { SST(0x73, 0x01, SS_RDEF, "Power calibration area almost full") }, /* R */ { SST(0x73, 0x02, SS_FATAL | ENOSPC, "Power calibration area is full") }, /* R */ { SST(0x73, 0x03, SS_RDEF, "Power calibration area error") }, /* R */ { SST(0x73, 0x04, SS_RDEF, "Program memory area update failure") }, /* R */ { SST(0x73, 0x05, SS_RDEF, "Program memory area is full") }, /* R */ { SST(0x73, 0x06, SS_RDEF, /* XXX TBD */ "RMA/PMA is almost full") }, /* R */ { SST(0x73, 0x10, SS_RDEF, /* XXX TBD */ "Current power calibration area almost full") }, /* R */ { SST(0x73, 0x11, SS_RDEF, /* XXX TBD */ "Current power calibration area is full") }, /* R */ { SST(0x73, 0x17, SS_RDEF, /* XXX TBD */ "RDZ is full") }, /* T */ { SST(0x74, 0x00, SS_RDEF, /* XXX TBD */ "Security error") }, /* T */ { SST(0x74, 0x01, SS_RDEF, /* XXX TBD */ "Unable to decrypt data") }, /* T */ { SST(0x74, 0x02, SS_RDEF, /* XXX TBD */ "Unencrypted data encountered while decrypting") }, /* T */ { SST(0x74, 0x03, SS_RDEF, /* XXX TBD */ "Incorrect data encryption key") }, /* T */ { SST(0x74, 0x04, SS_RDEF, /* XXX TBD */ "Cryptographic integrity validation failed") }, /* T */ { SST(0x74, 0x05, SS_RDEF, /* XXX TBD */ "Error decrypting data") }, /* T */ { SST(0x74, 0x06, SS_RDEF, /* XXX TBD */ "Unknown signature verification key") }, /* T */ { SST(0x74, 0x07, SS_RDEF, /* XXX TBD */ "Encryption parameters not useable") }, /* DT R M E VF */ { SST(0x74, 0x08, SS_RDEF, /* XXX TBD */ "Digital signature validation failure") }, /* T */ { SST(0x74, 0x09, SS_RDEF, /* XXX TBD */ "Encryption mode mismatch on read") }, /* T */ { SST(0x74, 0x0A, SS_RDEF, /* XXX TBD */ "Encrypted block not raw read enabled") }, /* T */ { SST(0x74, 0x0B, SS_RDEF, /* XXX TBD */ "Incorrect encryption parameters") }, /* DT R MAEBKV */ { SST(0x74, 0x0C, SS_RDEF, /* XXX TBD */ "Unable to decrypt parameter list") }, /* T */ { SST(0x74, 0x0D, SS_RDEF, /* XXX TBD */ "Encryption algorithm disabled") }, /* DT R MAEBKV */ { SST(0x74, 0x10, SS_RDEF, /* XXX TBD */ "SA creation parameter value invalid") }, /* DT R MAEBKV */ { SST(0x74, 0x11, SS_RDEF, /* XXX TBD */ "SA creation parameter value rejected") }, /* DT R MAEBKV */ { SST(0x74, 0x12, SS_RDEF, /* XXX TBD */ "Invalid SA usage") }, /* T */ { SST(0x74, 0x21, SS_RDEF, /* XXX TBD */ "Data encryption configuration prevented") }, /* DT R MAEBKV */ { SST(0x74, 0x30, SS_RDEF, /* XXX TBD */ "SA creation parameter not supported") }, /* DT R MAEBKV */ { SST(0x74, 0x40, SS_RDEF, /* XXX TBD */ "Authentication failed") }, /* V */ { SST(0x74, 0x61, SS_RDEF, /* XXX TBD */ "External data encryption key manager access error") }, /* V */ { SST(0x74, 0x62, SS_RDEF, /* XXX TBD */ "External data encryption key manager error") }, /* V */ { SST(0x74, 0x63, SS_RDEF, /* XXX TBD */ "External data encryption key not found") }, /* V */ { SST(0x74, 0x64, SS_RDEF, /* XXX TBD */ "External data encryption request not authorized") }, /* T */ { SST(0x74, 0x6E, SS_RDEF, /* XXX TBD */ "External data encryption control timeout") }, /* T */ { SST(0x74, 0x6F, SS_RDEF, /* XXX TBD */ "External data encryption control error") }, /* DT R M E V */ { SST(0x74, 0x71, SS_FATAL | EACCES, "Logical unit access not authorized") }, /* D */ { SST(0x74, 0x79, SS_FATAL | EACCES, "Security conflict in translated device") } }; const u_int asc_table_size = nitems(asc_table); struct asc_key { int asc; int ascq; }; static int ascentrycomp(const void *key, const void *member) { int asc; int ascq; const struct asc_table_entry *table_entry; asc = ((const struct asc_key *)key)->asc; ascq = ((const struct asc_key *)key)->ascq; table_entry = (const struct asc_table_entry *)member; if (asc >= table_entry->asc) { if (asc > table_entry->asc) return (1); if (ascq <= table_entry->ascq) { /* Check for ranges */ if (ascq == table_entry->ascq || ((table_entry->action & SSQ_RANGE) != 0 && ascq >= (table_entry - 1)->ascq)) return (0); return (-1); } return (1); } return (-1); } static int senseentrycomp(const void *key, const void *member) { int sense_key; const struct sense_key_table_entry *table_entry; sense_key = *((const int *)key); table_entry = (const struct sense_key_table_entry *)member; if (sense_key >= table_entry->sense_key) { if (sense_key == table_entry->sense_key) return (0); return (1); } return (-1); } static void fetchtableentries(int sense_key, int asc, int ascq, struct scsi_inquiry_data *inq_data, const struct sense_key_table_entry **sense_entry, const struct asc_table_entry **asc_entry) { caddr_t match; const struct asc_table_entry *asc_tables[2]; const struct sense_key_table_entry *sense_tables[2]; struct asc_key asc_ascq; size_t asc_tables_size[2]; size_t sense_tables_size[2]; int num_asc_tables; int num_sense_tables; int i; /* Default to failure */ *sense_entry = NULL; *asc_entry = NULL; match = NULL; if (inq_data != NULL) match = cam_quirkmatch((caddr_t)inq_data, (caddr_t)sense_quirk_table, sense_quirk_table_size, sizeof(*sense_quirk_table), scsi_inquiry_match); if (match != NULL) { struct scsi_sense_quirk_entry *quirk; quirk = (struct scsi_sense_quirk_entry *)match; asc_tables[0] = quirk->asc_info; asc_tables_size[0] = quirk->num_ascs; asc_tables[1] = asc_table; asc_tables_size[1] = asc_table_size; num_asc_tables = 2; sense_tables[0] = quirk->sense_key_info; sense_tables_size[0] = quirk->num_sense_keys; sense_tables[1] = sense_key_table; sense_tables_size[1] = nitems(sense_key_table); num_sense_tables = 2; } else { asc_tables[0] = asc_table; asc_tables_size[0] = asc_table_size; num_asc_tables = 1; sense_tables[0] = sense_key_table; sense_tables_size[0] = nitems(sense_key_table); num_sense_tables = 1; } asc_ascq.asc = asc; asc_ascq.ascq = ascq; for (i = 0; i < num_asc_tables; i++) { void *found_entry; found_entry = bsearch(&asc_ascq, asc_tables[i], asc_tables_size[i], sizeof(**asc_tables), ascentrycomp); if (found_entry) { *asc_entry = (struct asc_table_entry *)found_entry; break; } } for (i = 0; i < num_sense_tables; i++) { void *found_entry; found_entry = bsearch(&sense_key, sense_tables[i], sense_tables_size[i], sizeof(**sense_tables), senseentrycomp); if (found_entry) { *sense_entry = (struct sense_key_table_entry *)found_entry; break; } } } void scsi_sense_desc(int sense_key, int asc, int ascq, struct scsi_inquiry_data *inq_data, const char **sense_key_desc, const char **asc_desc) { const struct asc_table_entry *asc_entry; const struct sense_key_table_entry *sense_entry; fetchtableentries(sense_key, asc, ascq, inq_data, &sense_entry, &asc_entry); if (sense_entry != NULL) *sense_key_desc = sense_entry->desc; else *sense_key_desc = "Invalid Sense Key"; if (asc_entry != NULL) *asc_desc = asc_entry->desc; else if (asc >= 0x80 && asc <= 0xff) *asc_desc = "Vendor Specific ASC"; else if (ascq >= 0x80 && ascq <= 0xff) *asc_desc = "Vendor Specific ASCQ"; else *asc_desc = "Reserved ASC/ASCQ pair"; } /* * Given sense and device type information, return the appropriate action. * If we do not understand the specific error as identified by the ASC/ASCQ * pair, fall back on the more generic actions derived from the sense key. */ scsi_sense_action scsi_error_action(struct ccb_scsiio *csio, struct scsi_inquiry_data *inq_data, u_int32_t sense_flags) { const struct asc_table_entry *asc_entry; const struct sense_key_table_entry *sense_entry; int error_code, sense_key, asc, ascq; scsi_sense_action action; if (!scsi_extract_sense_ccb((union ccb *)csio, &error_code, &sense_key, &asc, &ascq)) { action = SS_RETRY | SSQ_DECREMENT_COUNT | SSQ_PRINT_SENSE | EIO; } else if ((error_code == SSD_DEFERRED_ERROR) || (error_code == SSD_DESC_DEFERRED_ERROR)) { /* * XXX dufault@FreeBSD.org * This error doesn't relate to the command associated * with this request sense. A deferred error is an error * for a command that has already returned GOOD status * (see SCSI2 8.2.14.2). * * By my reading of that section, it looks like the current * command has been cancelled, we should now clean things up * (hopefully recovering any lost data) and then retry the * current command. There are two easy choices, both wrong: * * 1. Drop through (like we had been doing), thus treating * this as if the error were for the current command and * return and stop the current command. * * 2. Issue a retry (like I made it do) thus hopefully * recovering the current transfer, and ignoring the * fact that we've dropped a command. * * These should probably be handled in a device specific * sense handler or punted back up to a user mode daemon */ action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE; } else { fetchtableentries(sense_key, asc, ascq, inq_data, &sense_entry, &asc_entry); /* * Override the 'No additional Sense' entry (0,0) * with the error action of the sense key. */ if (asc_entry != NULL && (asc != 0 || ascq != 0)) action = asc_entry->action; else if (sense_entry != NULL) action = sense_entry->action; else action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE; if (sense_key == SSD_KEY_RECOVERED_ERROR) { /* * The action succeeded but the device wants * the user to know that some recovery action * was required. */ action &= ~(SS_MASK|SSQ_MASK|SS_ERRMASK); action |= SS_NOP|SSQ_PRINT_SENSE; } else if (sense_key == SSD_KEY_ILLEGAL_REQUEST) { if ((sense_flags & SF_QUIET_IR) != 0) action &= ~SSQ_PRINT_SENSE; } else if (sense_key == SSD_KEY_UNIT_ATTENTION) { if ((sense_flags & SF_RETRY_UA) != 0 && (action & SS_MASK) == SS_FAIL) { action &= ~(SS_MASK|SSQ_MASK); action |= SS_RETRY|SSQ_DECREMENT_COUNT| SSQ_PRINT_SENSE; } action |= SSQ_UA; } } if ((action & SS_MASK) >= SS_START && (sense_flags & SF_NO_RECOVERY)) { action &= ~SS_MASK; action |= SS_FAIL; } else if ((action & SS_MASK) == SS_RETRY && (sense_flags & SF_NO_RETRY)) { action &= ~SS_MASK; action |= SS_FAIL; } if ((sense_flags & SF_PRINT_ALWAYS) != 0) action |= SSQ_PRINT_SENSE; else if ((sense_flags & SF_NO_PRINT) != 0) action &= ~SSQ_PRINT_SENSE; return (action); } char * scsi_cdb_string(u_int8_t *cdb_ptr, char *cdb_string, size_t len) { struct sbuf sb; int error; if (len == 0) return (""); sbuf_new(&sb, cdb_string, len, SBUF_FIXEDLEN); scsi_cdb_sbuf(cdb_ptr, &sb); /* ENOMEM just means that the fixed buffer is full, OK to ignore */ error = sbuf_finish(&sb); if (error != 0 && error != ENOMEM) return (""); return(sbuf_data(&sb)); } void scsi_cdb_sbuf(u_int8_t *cdb_ptr, struct sbuf *sb) { u_int8_t cdb_len; int i; if (cdb_ptr == NULL) return; /* * This is taken from the SCSI-3 draft spec. * (T10/1157D revision 0.3) * The top 3 bits of an opcode are the group code. The next 5 bits * are the command code. * Group 0: six byte commands * Group 1: ten byte commands * Group 2: ten byte commands * Group 3: reserved * Group 4: sixteen byte commands * Group 5: twelve byte commands * Group 6: vendor specific * Group 7: vendor specific */ switch((*cdb_ptr >> 5) & 0x7) { case 0: cdb_len = 6; break; case 1: case 2: cdb_len = 10; break; case 3: case 6: case 7: /* in this case, just print out the opcode */ cdb_len = 1; break; case 4: cdb_len = 16; break; case 5: cdb_len = 12; break; } for (i = 0; i < cdb_len; i++) sbuf_printf(sb, "%02hhx ", cdb_ptr[i]); return; } const char * scsi_status_string(struct ccb_scsiio *csio) { switch(csio->scsi_status) { case SCSI_STATUS_OK: return("OK"); case SCSI_STATUS_CHECK_COND: return("Check Condition"); case SCSI_STATUS_BUSY: return("Busy"); case SCSI_STATUS_INTERMED: return("Intermediate"); case SCSI_STATUS_INTERMED_COND_MET: return("Intermediate-Condition Met"); case SCSI_STATUS_RESERV_CONFLICT: return("Reservation Conflict"); case SCSI_STATUS_CMD_TERMINATED: return("Command Terminated"); case SCSI_STATUS_QUEUE_FULL: return("Queue Full"); case SCSI_STATUS_ACA_ACTIVE: return("ACA Active"); case SCSI_STATUS_TASK_ABORTED: return("Task Aborted"); default: { static char unkstr[64]; snprintf(unkstr, sizeof(unkstr), "Unknown %#x", csio->scsi_status); return(unkstr); } } } /* * scsi_command_string() returns 0 for success and -1 for failure. */ #ifdef _KERNEL int scsi_command_string(struct ccb_scsiio *csio, struct sbuf *sb) #else /* !_KERNEL */ int scsi_command_string(struct cam_device *device, struct ccb_scsiio *csio, struct sbuf *sb) #endif /* _KERNEL/!_KERNEL */ { struct scsi_inquiry_data *inq_data; #ifdef _KERNEL struct ccb_getdev *cgd; #endif /* _KERNEL */ #ifdef _KERNEL if ((cgd = (struct ccb_getdev*)xpt_alloc_ccb_nowait()) == NULL) return(-1); /* * Get the device information. */ xpt_setup_ccb(&cgd->ccb_h, csio->ccb_h.path, CAM_PRIORITY_NORMAL); cgd->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)cgd); /* * If the device is unconfigured, just pretend that it is a hard * drive. scsi_op_desc() needs this. */ if (cgd->ccb_h.status == CAM_DEV_NOT_THERE) cgd->inq_data.device = T_DIRECT; inq_data = &cgd->inq_data; #else /* !_KERNEL */ inq_data = &device->inq_data; #endif /* _KERNEL/!_KERNEL */ sbuf_printf(sb, "%s. CDB: ", scsi_op_desc(scsiio_cdb_ptr(csio)[0], inq_data)); scsi_cdb_sbuf(scsiio_cdb_ptr(csio), sb); #ifdef _KERNEL xpt_free_ccb((union ccb *)cgd); #endif return(0); } /* * Iterate over sense descriptors. Each descriptor is passed into iter_func(). * If iter_func() returns 0, list traversal continues. If iter_func() * returns non-zero, list traversal is stopped. */ void scsi_desc_iterate(struct scsi_sense_data_desc *sense, u_int sense_len, int (*iter_func)(struct scsi_sense_data_desc *sense, u_int, struct scsi_sense_desc_header *, void *), void *arg) { int cur_pos; int desc_len; /* * First make sure the extra length field is present. */ if (SSD_DESC_IS_PRESENT(sense, sense_len, extra_len) == 0) return; /* * The length of data actually returned may be different than the * extra_len recorded in the structure. */ desc_len = sense_len -offsetof(struct scsi_sense_data_desc, sense_desc); /* * Limit this further by the extra length reported, and the maximum * allowed extra length. */ desc_len = MIN(desc_len, MIN(sense->extra_len, SSD_EXTRA_MAX)); /* * Subtract the size of the header from the descriptor length. * This is to ensure that we have at least the header left, so we * don't have to check that inside the loop. This can wind up * being a negative value. */ desc_len -= sizeof(struct scsi_sense_desc_header); for (cur_pos = 0; cur_pos < desc_len;) { struct scsi_sense_desc_header *header; header = (struct scsi_sense_desc_header *) &sense->sense_desc[cur_pos]; /* * Check to make sure we have the entire descriptor. We * don't call iter_func() unless we do. * * Note that although cur_pos is at the beginning of the * descriptor, desc_len already has the header length * subtracted. So the comparison of the length in the * header (which does not include the header itself) to * desc_len - cur_pos is correct. */ if (header->length > (desc_len - cur_pos)) break; if (iter_func(sense, sense_len, header, arg) != 0) break; cur_pos += sizeof(*header) + header->length; } } struct scsi_find_desc_info { uint8_t desc_type; struct scsi_sense_desc_header *header; }; static int scsi_find_desc_func(struct scsi_sense_data_desc *sense, u_int sense_len, struct scsi_sense_desc_header *header, void *arg) { struct scsi_find_desc_info *desc_info; desc_info = (struct scsi_find_desc_info *)arg; if (header->desc_type == desc_info->desc_type) { desc_info->header = header; /* We found the descriptor, tell the iterator to stop. */ return (1); } else return (0); } /* * Given a descriptor type, return a pointer to it if it is in the sense * data and not truncated. Avoiding truncating sense data will simplify * things significantly for the caller. */ uint8_t * scsi_find_desc(struct scsi_sense_data_desc *sense, u_int sense_len, uint8_t desc_type) { struct scsi_find_desc_info desc_info; desc_info.desc_type = desc_type; desc_info.header = NULL; scsi_desc_iterate(sense, sense_len, scsi_find_desc_func, &desc_info); return ((uint8_t *)desc_info.header); } /* * Fill in SCSI descriptor sense data with the specified parameters. */ static void scsi_set_sense_data_desc_va(struct scsi_sense_data *sense_data, u_int *sense_len, scsi_sense_data_type sense_format, int current_error, int sense_key, int asc, int ascq, va_list ap) { struct scsi_sense_data_desc *sense; scsi_sense_elem_type elem_type; int space, len; uint8_t *desc, *data; memset(sense_data, 0, sizeof(*sense_data)); sense = (struct scsi_sense_data_desc *)sense_data; if (current_error != 0) sense->error_code = SSD_DESC_CURRENT_ERROR; else sense->error_code = SSD_DESC_DEFERRED_ERROR; sense->sense_key = sense_key; sense->add_sense_code = asc; sense->add_sense_code_qual = ascq; sense->flags = 0; desc = &sense->sense_desc[0]; space = *sense_len - offsetof(struct scsi_sense_data_desc, sense_desc); while ((elem_type = va_arg(ap, scsi_sense_elem_type)) != SSD_ELEM_NONE) { if (elem_type >= SSD_ELEM_MAX) { printf("%s: invalid sense type %d\n", __func__, elem_type); break; } len = va_arg(ap, int); data = va_arg(ap, uint8_t *); switch (elem_type) { case SSD_ELEM_SKIP: break; case SSD_ELEM_DESC: if (space < len) { sense->flags |= SSDD_SDAT_OVFL; break; } bcopy(data, desc, len); desc += len; space -= len; break; case SSD_ELEM_SKS: { struct scsi_sense_sks *sks = (void *)desc; if (len > sizeof(sks->sense_key_spec)) break; if (space < sizeof(*sks)) { sense->flags |= SSDD_SDAT_OVFL; break; } sks->desc_type = SSD_DESC_SKS; sks->length = sizeof(*sks) - (offsetof(struct scsi_sense_sks, length) + 1); bcopy(data, &sks->sense_key_spec, len); desc += sizeof(*sks); space -= sizeof(*sks); break; } case SSD_ELEM_COMMAND: { struct scsi_sense_command *cmd = (void *)desc; if (len > sizeof(cmd->command_info)) break; if (space < sizeof(*cmd)) { sense->flags |= SSDD_SDAT_OVFL; break; } cmd->desc_type = SSD_DESC_COMMAND; cmd->length = sizeof(*cmd) - (offsetof(struct scsi_sense_command, length) + 1); bcopy(data, &cmd->command_info[ sizeof(cmd->command_info) - len], len); desc += sizeof(*cmd); space -= sizeof(*cmd); break; } case SSD_ELEM_INFO: { struct scsi_sense_info *info = (void *)desc; if (len > sizeof(info->info)) break; if (space < sizeof(*info)) { sense->flags |= SSDD_SDAT_OVFL; break; } info->desc_type = SSD_DESC_INFO; info->length = sizeof(*info) - (offsetof(struct scsi_sense_info, length) + 1); info->byte2 = SSD_INFO_VALID; bcopy(data, &info->info[sizeof(info->info) - len], len); desc += sizeof(*info); space -= sizeof(*info); break; } case SSD_ELEM_FRU: { struct scsi_sense_fru *fru = (void *)desc; if (len > sizeof(fru->fru)) break; if (space < sizeof(*fru)) { sense->flags |= SSDD_SDAT_OVFL; break; } fru->desc_type = SSD_DESC_FRU; fru->length = sizeof(*fru) - (offsetof(struct scsi_sense_fru, length) + 1); fru->fru = *data; desc += sizeof(*fru); space -= sizeof(*fru); break; } case SSD_ELEM_STREAM: { struct scsi_sense_stream *stream = (void *)desc; if (len > sizeof(stream->byte3)) break; if (space < sizeof(*stream)) { sense->flags |= SSDD_SDAT_OVFL; break; } stream->desc_type = SSD_DESC_STREAM; stream->length = sizeof(*stream) - (offsetof(struct scsi_sense_stream, length) + 1); stream->byte3 = *data; desc += sizeof(*stream); space -= sizeof(*stream); break; } default: /* * We shouldn't get here, but if we do, do nothing. * We've already consumed the arguments above. */ break; } } sense->extra_len = desc - &sense->sense_desc[0]; *sense_len = offsetof(struct scsi_sense_data_desc, extra_len) + 1 + sense->extra_len; } /* * Fill in SCSI fixed sense data with the specified parameters. */ static void scsi_set_sense_data_fixed_va(struct scsi_sense_data *sense_data, u_int *sense_len, scsi_sense_data_type sense_format, int current_error, int sense_key, int asc, int ascq, va_list ap) { struct scsi_sense_data_fixed *sense; scsi_sense_elem_type elem_type; uint8_t *data; int len; memset(sense_data, 0, sizeof(*sense_data)); sense = (struct scsi_sense_data_fixed *)sense_data; if (current_error != 0) sense->error_code = SSD_CURRENT_ERROR; else sense->error_code = SSD_DEFERRED_ERROR; sense->flags = sense_key & SSD_KEY; sense->extra_len = 0; if (*sense_len >= 13) { sense->add_sense_code = asc; sense->extra_len = MAX(sense->extra_len, 5); } else sense->flags |= SSD_SDAT_OVFL; if (*sense_len >= 14) { sense->add_sense_code_qual = ascq; sense->extra_len = MAX(sense->extra_len, 6); } else sense->flags |= SSD_SDAT_OVFL; while ((elem_type = va_arg(ap, scsi_sense_elem_type)) != SSD_ELEM_NONE) { if (elem_type >= SSD_ELEM_MAX) { printf("%s: invalid sense type %d\n", __func__, elem_type); break; } len = va_arg(ap, int); data = va_arg(ap, uint8_t *); switch (elem_type) { case SSD_ELEM_SKIP: break; case SSD_ELEM_SKS: if (len > sizeof(sense->sense_key_spec)) break; if (*sense_len < 18) { sense->flags |= SSD_SDAT_OVFL; break; } bcopy(data, &sense->sense_key_spec[0], len); sense->extra_len = MAX(sense->extra_len, 10); break; case SSD_ELEM_COMMAND: if (*sense_len < 12) { sense->flags |= SSD_SDAT_OVFL; break; } if (len > sizeof(sense->cmd_spec_info)) { data += len - sizeof(sense->cmd_spec_info); len -= len - sizeof(sense->cmd_spec_info); } bcopy(data, &sense->cmd_spec_info[ sizeof(sense->cmd_spec_info) - len], len); sense->extra_len = MAX(sense->extra_len, 4); break; case SSD_ELEM_INFO: /* Set VALID bit only if no overflow. */ sense->error_code |= SSD_ERRCODE_VALID; while (len > sizeof(sense->info)) { if (data[0] != 0) sense->error_code &= ~SSD_ERRCODE_VALID; data ++; len --; } bcopy(data, &sense->info[sizeof(sense->info) - len], len); break; case SSD_ELEM_FRU: if (*sense_len < 15) { sense->flags |= SSD_SDAT_OVFL; break; } sense->fru = *data; sense->extra_len = MAX(sense->extra_len, 7); break; case SSD_ELEM_STREAM: sense->flags |= *data & (SSD_ILI | SSD_EOM | SSD_FILEMARK); break; default: /* * We can't handle that in fixed format. Skip it. */ break; } } *sense_len = offsetof(struct scsi_sense_data_fixed, extra_len) + 1 + sense->extra_len; } /* * Fill in SCSI sense data with the specified parameters. This routine can * fill in either fixed or descriptor type sense data. */ void scsi_set_sense_data_va(struct scsi_sense_data *sense_data, u_int *sense_len, scsi_sense_data_type sense_format, int current_error, int sense_key, int asc, int ascq, va_list ap) { if (*sense_len > SSD_FULL_SIZE) *sense_len = SSD_FULL_SIZE; if (sense_format == SSD_TYPE_DESC) scsi_set_sense_data_desc_va(sense_data, sense_len, sense_format, current_error, sense_key, asc, ascq, ap); else scsi_set_sense_data_fixed_va(sense_data, sense_len, sense_format, current_error, sense_key, asc, ascq, ap); } void scsi_set_sense_data(struct scsi_sense_data *sense_data, scsi_sense_data_type sense_format, int current_error, int sense_key, int asc, int ascq, ...) { va_list ap; u_int sense_len = SSD_FULL_SIZE; va_start(ap, ascq); scsi_set_sense_data_va(sense_data, &sense_len, sense_format, current_error, sense_key, asc, ascq, ap); va_end(ap); } void scsi_set_sense_data_len(struct scsi_sense_data *sense_data, u_int *sense_len, scsi_sense_data_type sense_format, int current_error, int sense_key, int asc, int ascq, ...) { va_list ap; va_start(ap, ascq); scsi_set_sense_data_va(sense_data, sense_len, sense_format, current_error, sense_key, asc, ascq, ap); va_end(ap); } /* * Get sense information for three similar sense data types. */ int scsi_get_sense_info(struct scsi_sense_data *sense_data, u_int sense_len, uint8_t info_type, uint64_t *info, int64_t *signed_info) { scsi_sense_data_type sense_type; if (sense_len == 0) goto bailout; sense_type = scsi_sense_type(sense_data); switch (sense_type) { case SSD_TYPE_DESC: { struct scsi_sense_data_desc *sense; uint8_t *desc; sense = (struct scsi_sense_data_desc *)sense_data; desc = scsi_find_desc(sense, sense_len, info_type); if (desc == NULL) goto bailout; switch (info_type) { case SSD_DESC_INFO: { struct scsi_sense_info *info_desc; info_desc = (struct scsi_sense_info *)desc; *info = scsi_8btou64(info_desc->info); if (signed_info != NULL) *signed_info = *info; break; } case SSD_DESC_COMMAND: { struct scsi_sense_command *cmd_desc; cmd_desc = (struct scsi_sense_command *)desc; *info = scsi_8btou64(cmd_desc->command_info); if (signed_info != NULL) *signed_info = *info; break; } case SSD_DESC_FRU: { struct scsi_sense_fru *fru_desc; fru_desc = (struct scsi_sense_fru *)desc; *info = fru_desc->fru; if (signed_info != NULL) *signed_info = (int8_t)fru_desc->fru; break; } default: goto bailout; break; } break; } case SSD_TYPE_FIXED: { struct scsi_sense_data_fixed *sense; sense = (struct scsi_sense_data_fixed *)sense_data; switch (info_type) { case SSD_DESC_INFO: { uint32_t info_val; if ((sense->error_code & SSD_ERRCODE_VALID) == 0) goto bailout; if (SSD_FIXED_IS_PRESENT(sense, sense_len, info) == 0) goto bailout; info_val = scsi_4btoul(sense->info); *info = info_val; if (signed_info != NULL) *signed_info = (int32_t)info_val; break; } case SSD_DESC_COMMAND: { uint32_t cmd_val; if ((SSD_FIXED_IS_PRESENT(sense, sense_len, cmd_spec_info) == 0) || (SSD_FIXED_IS_FILLED(sense, cmd_spec_info) == 0)) goto bailout; cmd_val = scsi_4btoul(sense->cmd_spec_info); if (cmd_val == 0) goto bailout; *info = cmd_val; if (signed_info != NULL) *signed_info = (int32_t)cmd_val; break; } case SSD_DESC_FRU: if ((SSD_FIXED_IS_PRESENT(sense, sense_len, fru) == 0) || (SSD_FIXED_IS_FILLED(sense, fru) == 0)) goto bailout; if (sense->fru == 0) goto bailout; *info = sense->fru; if (signed_info != NULL) *signed_info = (int8_t)sense->fru; break; default: goto bailout; break; } break; } default: goto bailout; break; } return (0); bailout: return (1); } int scsi_get_sks(struct scsi_sense_data *sense_data, u_int sense_len, uint8_t *sks) { scsi_sense_data_type sense_type; if (sense_len == 0) goto bailout; sense_type = scsi_sense_type(sense_data); switch (sense_type) { case SSD_TYPE_DESC: { struct scsi_sense_data_desc *sense; struct scsi_sense_sks *desc; sense = (struct scsi_sense_data_desc *)sense_data; desc = (struct scsi_sense_sks *)scsi_find_desc(sense, sense_len, SSD_DESC_SKS); if (desc == NULL) goto bailout; /* * No need to check the SKS valid bit for descriptor sense. * If the descriptor is present, it is valid. */ bcopy(desc->sense_key_spec, sks, sizeof(desc->sense_key_spec)); break; } case SSD_TYPE_FIXED: { struct scsi_sense_data_fixed *sense; sense = (struct scsi_sense_data_fixed *)sense_data; if ((SSD_FIXED_IS_PRESENT(sense, sense_len, sense_key_spec)== 0) || (SSD_FIXED_IS_FILLED(sense, sense_key_spec) == 0)) goto bailout; if ((sense->sense_key_spec[0] & SSD_SCS_VALID) == 0) goto bailout; bcopy(sense->sense_key_spec, sks,sizeof(sense->sense_key_spec)); break; } default: goto bailout; break; } return (0); bailout: return (1); } /* * Provide a common interface for fixed and descriptor sense to detect * whether we have block-specific sense information. It is clear by the * presence of the block descriptor in descriptor mode, but we have to * infer from the inquiry data and ILI bit in fixed mode. */ int scsi_get_block_info(struct scsi_sense_data *sense_data, u_int sense_len, struct scsi_inquiry_data *inq_data, uint8_t *block_bits) { scsi_sense_data_type sense_type; if (inq_data != NULL) { switch (SID_TYPE(inq_data)) { case T_DIRECT: case T_RBC: case T_ZBC_HM: break; default: goto bailout; break; } } sense_type = scsi_sense_type(sense_data); switch (sense_type) { case SSD_TYPE_DESC: { struct scsi_sense_data_desc *sense; struct scsi_sense_block *block; sense = (struct scsi_sense_data_desc *)sense_data; block = (struct scsi_sense_block *)scsi_find_desc(sense, sense_len, SSD_DESC_BLOCK); if (block == NULL) goto bailout; *block_bits = block->byte3; break; } case SSD_TYPE_FIXED: { struct scsi_sense_data_fixed *sense; sense = (struct scsi_sense_data_fixed *)sense_data; if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags) == 0) goto bailout; if ((sense->flags & SSD_ILI) == 0) goto bailout; *block_bits = sense->flags & SSD_ILI; break; } default: goto bailout; break; } return (0); bailout: return (1); } int scsi_get_stream_info(struct scsi_sense_data *sense_data, u_int sense_len, struct scsi_inquiry_data *inq_data, uint8_t *stream_bits) { scsi_sense_data_type sense_type; if (inq_data != NULL) { switch (SID_TYPE(inq_data)) { case T_SEQUENTIAL: break; default: goto bailout; break; } } sense_type = scsi_sense_type(sense_data); switch (sense_type) { case SSD_TYPE_DESC: { struct scsi_sense_data_desc *sense; struct scsi_sense_stream *stream; sense = (struct scsi_sense_data_desc *)sense_data; stream = (struct scsi_sense_stream *)scsi_find_desc(sense, sense_len, SSD_DESC_STREAM); if (stream == NULL) goto bailout; *stream_bits = stream->byte3; break; } case SSD_TYPE_FIXED: { struct scsi_sense_data_fixed *sense; sense = (struct scsi_sense_data_fixed *)sense_data; if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags) == 0) goto bailout; if ((sense->flags & (SSD_ILI|SSD_EOM|SSD_FILEMARK)) == 0) goto bailout; *stream_bits = sense->flags & (SSD_ILI|SSD_EOM|SSD_FILEMARK); break; } default: goto bailout; break; } return (0); bailout: return (1); } void scsi_info_sbuf(struct sbuf *sb, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, uint64_t info) { sbuf_printf(sb, "Info: %#jx", info); } void scsi_command_sbuf(struct sbuf *sb, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, uint64_t csi) { sbuf_printf(sb, "Command Specific Info: %#jx", csi); } void scsi_progress_sbuf(struct sbuf *sb, uint16_t progress) { sbuf_printf(sb, "Progress: %d%% (%d/%d) complete", (progress * 100) / SSD_SKS_PROGRESS_DENOM, progress, SSD_SKS_PROGRESS_DENOM); } /* * Returns 1 for failure (i.e. SKS isn't valid) and 0 for success. */ int scsi_sks_sbuf(struct sbuf *sb, int sense_key, uint8_t *sks) { if ((sks[0] & SSD_SKS_VALID) == 0) return (1); switch (sense_key) { case SSD_KEY_ILLEGAL_REQUEST: { struct scsi_sense_sks_field *field; int bad_command; char tmpstr[40]; /*Field Pointer*/ field = (struct scsi_sense_sks_field *)sks; if (field->byte0 & SSD_SKS_FIELD_CMD) bad_command = 1; else bad_command = 0; tmpstr[0] = '\0'; /* Bit pointer is valid */ if (field->byte0 & SSD_SKS_BPV) snprintf(tmpstr, sizeof(tmpstr), "bit %d ", field->byte0 & SSD_SKS_BIT_VALUE); sbuf_printf(sb, "%s byte %d %sis invalid", bad_command ? "Command" : "Data", scsi_2btoul(field->field), tmpstr); break; } case SSD_KEY_UNIT_ATTENTION: { struct scsi_sense_sks_overflow *overflow; overflow = (struct scsi_sense_sks_overflow *)sks; /*UA Condition Queue Overflow*/ sbuf_printf(sb, "Unit Attention Condition Queue %s", (overflow->byte0 & SSD_SKS_OVERFLOW_SET) ? "Overflowed" : "Did Not Overflow??"); break; } case SSD_KEY_RECOVERED_ERROR: case SSD_KEY_HARDWARE_ERROR: case SSD_KEY_MEDIUM_ERROR: { struct scsi_sense_sks_retry *retry; /*Actual Retry Count*/ retry = (struct scsi_sense_sks_retry *)sks; sbuf_printf(sb, "Actual Retry Count: %d", scsi_2btoul(retry->actual_retry_count)); break; } case SSD_KEY_NO_SENSE: case SSD_KEY_NOT_READY: { struct scsi_sense_sks_progress *progress; int progress_val; /*Progress Indication*/ progress = (struct scsi_sense_sks_progress *)sks; progress_val = scsi_2btoul(progress->progress); scsi_progress_sbuf(sb, progress_val); break; } case SSD_KEY_COPY_ABORTED: { struct scsi_sense_sks_segment *segment; char tmpstr[40]; /*Segment Pointer*/ segment = (struct scsi_sense_sks_segment *)sks; tmpstr[0] = '\0'; if (segment->byte0 & SSD_SKS_SEGMENT_BPV) snprintf(tmpstr, sizeof(tmpstr), "bit %d ", segment->byte0 & SSD_SKS_SEGMENT_BITPTR); sbuf_printf(sb, "%s byte %d %sis invalid", (segment->byte0 & SSD_SKS_SEGMENT_SD) ? "Segment" : "Data", scsi_2btoul(segment->field), tmpstr); break; } default: sbuf_printf(sb, "Sense Key Specific: %#x,%#x", sks[0], scsi_2btoul(&sks[1])); break; } return (0); } void scsi_fru_sbuf(struct sbuf *sb, uint64_t fru) { sbuf_printf(sb, "Field Replaceable Unit: %d", (int)fru); } void scsi_stream_sbuf(struct sbuf *sb, uint8_t stream_bits, uint64_t info) { int need_comma; need_comma = 0; /* * XXX KDM this needs more descriptive decoding. */ if (stream_bits & SSD_DESC_STREAM_FM) { sbuf_printf(sb, "Filemark"); need_comma = 1; } if (stream_bits & SSD_DESC_STREAM_EOM) { sbuf_printf(sb, "%sEOM", (need_comma) ? "," : ""); need_comma = 1; } if (stream_bits & SSD_DESC_STREAM_ILI) sbuf_printf(sb, "%sILI", (need_comma) ? "," : ""); sbuf_printf(sb, ": Info: %#jx", (uintmax_t) info); } void scsi_block_sbuf(struct sbuf *sb, uint8_t block_bits, uint64_t info) { if (block_bits & SSD_DESC_BLOCK_ILI) sbuf_printf(sb, "ILI: residue %#jx", (uintmax_t) info); } void scsi_sense_info_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_info *info; info = (struct scsi_sense_info *)header; scsi_info_sbuf(sb, cdb, cdb_len, inq_data, scsi_8btou64(info->info)); } void scsi_sense_command_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_command *command; command = (struct scsi_sense_command *)header; scsi_command_sbuf(sb, cdb, cdb_len, inq_data, scsi_8btou64(command->command_info)); } void scsi_sense_sks_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_sks *sks; int error_code, sense_key, asc, ascq; sks = (struct scsi_sense_sks *)header; scsi_extract_sense_len(sense, sense_len, &error_code, &sense_key, &asc, &ascq, /*show_errors*/ 1); scsi_sks_sbuf(sb, sense_key, sks->sense_key_spec); } void scsi_sense_fru_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_fru *fru; fru = (struct scsi_sense_fru *)header; scsi_fru_sbuf(sb, (uint64_t)fru->fru); } void scsi_sense_stream_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_stream *stream; uint64_t info; stream = (struct scsi_sense_stream *)header; info = 0; scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &info, NULL); scsi_stream_sbuf(sb, stream->byte3, info); } void scsi_sense_block_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_block *block; uint64_t info; block = (struct scsi_sense_block *)header; info = 0; scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &info, NULL); scsi_block_sbuf(sb, block->byte3, info); } void scsi_sense_progress_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_progress *progress; const char *sense_key_desc; const char *asc_desc; int progress_val; progress = (struct scsi_sense_progress *)header; /* * Get descriptions for the sense key, ASC, and ASCQ in the * progress descriptor. These could be different than the values * in the overall sense data. */ scsi_sense_desc(progress->sense_key, progress->add_sense_code, progress->add_sense_code_qual, inq_data, &sense_key_desc, &asc_desc); progress_val = scsi_2btoul(progress->progress); /* * The progress indicator is for the operation described by the * sense key, ASC, and ASCQ in the descriptor. */ sbuf_cat(sb, sense_key_desc); sbuf_printf(sb, " asc:%x,%x (%s): ", progress->add_sense_code, progress->add_sense_code_qual, asc_desc); scsi_progress_sbuf(sb, progress_val); } void scsi_sense_ata_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_ata_ret_desc *res; res = (struct scsi_sense_ata_ret_desc *)header; sbuf_printf(sb, "ATA status: %02x (%s%s%s%s%s%s%s%s), ", res->status, (res->status & 0x80) ? "BSY " : "", (res->status & 0x40) ? "DRDY " : "", (res->status & 0x20) ? "DF " : "", (res->status & 0x10) ? "SERV " : "", (res->status & 0x08) ? "DRQ " : "", (res->status & 0x04) ? "CORR " : "", (res->status & 0x02) ? "IDX " : "", (res->status & 0x01) ? "ERR" : ""); if (res->status & 1) { sbuf_printf(sb, "error: %02x (%s%s%s%s%s%s%s%s), ", res->error, (res->error & 0x80) ? "ICRC " : "", (res->error & 0x40) ? "UNC " : "", (res->error & 0x20) ? "MC " : "", (res->error & 0x10) ? "IDNF " : "", (res->error & 0x08) ? "MCR " : "", (res->error & 0x04) ? "ABRT " : "", (res->error & 0x02) ? "NM " : "", (res->error & 0x01) ? "ILI" : ""); } if (res->flags & SSD_DESC_ATA_FLAG_EXTEND) { sbuf_printf(sb, "count: %02x%02x, ", res->count_15_8, res->count_7_0); sbuf_printf(sb, "LBA: %02x%02x%02x%02x%02x%02x, ", res->lba_47_40, res->lba_39_32, res->lba_31_24, res->lba_23_16, res->lba_15_8, res->lba_7_0); } else { sbuf_printf(sb, "count: %02x, ", res->count_7_0); sbuf_printf(sb, "LBA: %02x%02x%02x, ", res->lba_23_16, res->lba_15_8, res->lba_7_0); } sbuf_printf(sb, "device: %02x, ", res->device); } void scsi_sense_forwarded_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { struct scsi_sense_forwarded *forwarded; const char *sense_key_desc; const char *asc_desc; int error_code, sense_key, asc, ascq; forwarded = (struct scsi_sense_forwarded *)header; scsi_extract_sense_len((struct scsi_sense_data *)forwarded->sense_data, forwarded->length - 2, &error_code, &sense_key, &asc, &ascq, 1); scsi_sense_desc(sense_key, asc, ascq, NULL, &sense_key_desc, &asc_desc); sbuf_printf(sb, "Forwarded sense: %s asc:%x,%x (%s): ", sense_key_desc, asc, ascq, asc_desc); } /* * Generic sense descriptor printing routine. This is used when we have * not yet implemented a specific printing routine for this descriptor. */ void scsi_sense_generic_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { int i; uint8_t *buf_ptr; sbuf_printf(sb, "Descriptor %#x:", header->desc_type); buf_ptr = (uint8_t *)&header[1]; for (i = 0; i < header->length; i++, buf_ptr++) sbuf_printf(sb, " %02x", *buf_ptr); } /* * Keep this list in numeric order. This speeds the array traversal. */ struct scsi_sense_desc_printer { uint8_t desc_type; /* * The function arguments here are the superset of what is needed * to print out various different descriptors. Command and * information descriptors need inquiry data and command type. * Sense key specific descriptors need the sense key. * * The sense, cdb, and inquiry data arguments may be NULL, but the * information printed may not be fully decoded as a result. */ void (*print_func)(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header); } scsi_sense_printers[] = { {SSD_DESC_INFO, scsi_sense_info_sbuf}, {SSD_DESC_COMMAND, scsi_sense_command_sbuf}, {SSD_DESC_SKS, scsi_sense_sks_sbuf}, {SSD_DESC_FRU, scsi_sense_fru_sbuf}, {SSD_DESC_STREAM, scsi_sense_stream_sbuf}, {SSD_DESC_BLOCK, scsi_sense_block_sbuf}, {SSD_DESC_ATA, scsi_sense_ata_sbuf}, {SSD_DESC_PROGRESS, scsi_sense_progress_sbuf}, {SSD_DESC_FORWARDED, scsi_sense_forwarded_sbuf} }; void scsi_sense_desc_sbuf(struct sbuf *sb, struct scsi_sense_data *sense, u_int sense_len, uint8_t *cdb, int cdb_len, struct scsi_inquiry_data *inq_data, struct scsi_sense_desc_header *header) { u_int i; for (i = 0; i < nitems(scsi_sense_printers); i++) { struct scsi_sense_desc_printer *printer; printer = &scsi_sense_printers[i]; /* * The list is sorted, so quit if we've passed our * descriptor number. */ if (printer->desc_type > header->desc_type) break; if (printer->desc_type != header->desc_type) continue; printer->print_func(sb, sense, sense_len, cdb, cdb_len, inq_data, header); return; } /* * No specific printing routine, so use the generic routine. */ scsi_sense_generic_sbuf(sb, sense, sense_len, cdb, cdb_len, inq_data, header); } scsi_sense_data_type scsi_sense_type(struct scsi_sense_data *sense_data) { switch (sense_data->error_code & SSD_ERRCODE) { case SSD_DESC_CURRENT_ERROR: case SSD_DESC_DEFERRED_ERROR: return (SSD_TYPE_DESC); break; case SSD_CURRENT_ERROR: case SSD_DEFERRED_ERROR: return (SSD_TYPE_FIXED); break; default: break; } return (SSD_TYPE_NONE); } struct scsi_print_sense_info { struct sbuf *sb; char *path_str; uint8_t *cdb; int cdb_len; struct scsi_inquiry_data *inq_data; }; static int scsi_print_desc_func(struct scsi_sense_data_desc *sense, u_int sense_len, struct scsi_sense_desc_header *header, void *arg) { struct scsi_print_sense_info *print_info; print_info = (struct scsi_print_sense_info *)arg; switch (header->desc_type) { case SSD_DESC_INFO: case SSD_DESC_FRU: case SSD_DESC_COMMAND: case SSD_DESC_SKS: case SSD_DESC_BLOCK: case SSD_DESC_STREAM: /* * We have already printed these descriptors, if they are * present. */ break; default: { sbuf_printf(print_info->sb, "%s", print_info->path_str); scsi_sense_desc_sbuf(print_info->sb, (struct scsi_sense_data *)sense, sense_len, print_info->cdb, print_info->cdb_len, print_info->inq_data, header); sbuf_printf(print_info->sb, "\n"); break; } } /* * Tell the iterator that we want to see more descriptors if they * are present. */ return (0); } void scsi_sense_only_sbuf(struct scsi_sense_data *sense, u_int sense_len, struct sbuf *sb, char *path_str, struct scsi_inquiry_data *inq_data, uint8_t *cdb, int cdb_len) { int error_code, sense_key, asc, ascq; sbuf_cat(sb, path_str); scsi_extract_sense_len(sense, sense_len, &error_code, &sense_key, &asc, &ascq, /*show_errors*/ 1); sbuf_printf(sb, "SCSI sense: "); switch (error_code) { case SSD_DEFERRED_ERROR: case SSD_DESC_DEFERRED_ERROR: sbuf_printf(sb, "Deferred error: "); /* FALLTHROUGH */ case SSD_CURRENT_ERROR: case SSD_DESC_CURRENT_ERROR: { struct scsi_sense_data_desc *desc_sense; struct scsi_print_sense_info print_info; const char *sense_key_desc; const char *asc_desc; uint8_t sks[3]; uint64_t val; int info_valid; /* * Get descriptions for the sense key, ASC, and ASCQ. If * these aren't present in the sense data (i.e. the sense * data isn't long enough), the -1 values that * scsi_extract_sense_len() returns will yield default * or error descriptions. */ scsi_sense_desc(sense_key, asc, ascq, inq_data, &sense_key_desc, &asc_desc); /* * We first print the sense key and ASC/ASCQ. */ sbuf_cat(sb, sense_key_desc); sbuf_printf(sb, " asc:%x,%x (%s)\n", asc, ascq, asc_desc); /* * Get the info field if it is valid. */ if (scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &val, NULL) == 0) info_valid = 1; else info_valid = 0; if (info_valid != 0) { uint8_t bits; /* * Determine whether we have any block or stream * device-specific information. */ if (scsi_get_block_info(sense, sense_len, inq_data, &bits) == 0) { sbuf_cat(sb, path_str); scsi_block_sbuf(sb, bits, val); sbuf_printf(sb, "\n"); } else if (scsi_get_stream_info(sense, sense_len, inq_data, &bits) == 0) { sbuf_cat(sb, path_str); scsi_stream_sbuf(sb, bits, val); sbuf_printf(sb, "\n"); } else if (val != 0) { /* * The information field can be valid but 0. * If the block or stream bits aren't set, * and this is 0, it isn't terribly useful * to print it out. */ sbuf_cat(sb, path_str); scsi_info_sbuf(sb, cdb, cdb_len, inq_data, val); sbuf_printf(sb, "\n"); } } /* * Print the FRU. */ if (scsi_get_sense_info(sense, sense_len, SSD_DESC_FRU, &val, NULL) == 0) { sbuf_cat(sb, path_str); scsi_fru_sbuf(sb, val); sbuf_printf(sb, "\n"); } /* * Print any command-specific information. */ if (scsi_get_sense_info(sense, sense_len, SSD_DESC_COMMAND, &val, NULL) == 0) { sbuf_cat(sb, path_str); scsi_command_sbuf(sb, cdb, cdb_len, inq_data, val); sbuf_printf(sb, "\n"); } /* * Print out any sense-key-specific information. */ if (scsi_get_sks(sense, sense_len, sks) == 0) { sbuf_cat(sb, path_str); scsi_sks_sbuf(sb, sense_key, sks); sbuf_printf(sb, "\n"); } /* * If this is fixed sense, we're done. If we have * descriptor sense, we might have more information * available. */ if (scsi_sense_type(sense) != SSD_TYPE_DESC) break; desc_sense = (struct scsi_sense_data_desc *)sense; print_info.sb = sb; print_info.path_str = path_str; print_info.cdb = cdb; print_info.cdb_len = cdb_len; print_info.inq_data = inq_data; /* * Print any sense descriptors that we have not already printed. */ scsi_desc_iterate(desc_sense, sense_len, scsi_print_desc_func, &print_info); break; } case -1: /* * scsi_extract_sense_len() sets values to -1 if the * show_errors flag is set and they aren't present in the * sense data. This means that sense_len is 0. */ sbuf_printf(sb, "No sense data present\n"); break; default: { sbuf_printf(sb, "Error code 0x%x", error_code); if (sense->error_code & SSD_ERRCODE_VALID) { struct scsi_sense_data_fixed *fixed_sense; fixed_sense = (struct scsi_sense_data_fixed *)sense; if (SSD_FIXED_IS_PRESENT(fixed_sense, sense_len, info)){ uint32_t info; info = scsi_4btoul(fixed_sense->info); sbuf_printf(sb, " at block no. %d (decimal)", info); } } sbuf_printf(sb, "\n"); break; } } } /* * scsi_sense_sbuf() returns 0 for success and -1 for failure. */ #ifdef _KERNEL int scsi_sense_sbuf(struct ccb_scsiio *csio, struct sbuf *sb, scsi_sense_string_flags flags) #else /* !_KERNEL */ int scsi_sense_sbuf(struct cam_device *device, struct ccb_scsiio *csio, struct sbuf *sb, scsi_sense_string_flags flags) #endif /* _KERNEL/!_KERNEL */ { struct scsi_sense_data *sense; struct scsi_inquiry_data *inq_data; #ifdef _KERNEL struct ccb_getdev *cgd; #endif /* _KERNEL */ char path_str[64]; #ifndef _KERNEL if (device == NULL) return(-1); #endif /* !_KERNEL */ if ((csio == NULL) || (sb == NULL)) return(-1); /* * If the CDB is a physical address, we can't deal with it.. */ if ((csio->ccb_h.flags & CAM_CDB_PHYS) != 0) flags &= ~SSS_FLAG_PRINT_COMMAND; #ifdef _KERNEL xpt_path_string(csio->ccb_h.path, path_str, sizeof(path_str)); #else /* !_KERNEL */ cam_path_string(device, path_str, sizeof(path_str)); #endif /* _KERNEL/!_KERNEL */ #ifdef _KERNEL if ((cgd = (struct ccb_getdev*)xpt_alloc_ccb_nowait()) == NULL) return(-1); /* * Get the device information. */ xpt_setup_ccb(&cgd->ccb_h, csio->ccb_h.path, CAM_PRIORITY_NORMAL); cgd->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)cgd); /* * If the device is unconfigured, just pretend that it is a hard * drive. scsi_op_desc() needs this. */ if (cgd->ccb_h.status == CAM_DEV_NOT_THERE) cgd->inq_data.device = T_DIRECT; inq_data = &cgd->inq_data; #else /* !_KERNEL */ inq_data = &device->inq_data; #endif /* _KERNEL/!_KERNEL */ sense = NULL; if (flags & SSS_FLAG_PRINT_COMMAND) { sbuf_cat(sb, path_str); #ifdef _KERNEL scsi_command_string(csio, sb); #else /* !_KERNEL */ scsi_command_string(device, csio, sb); #endif /* _KERNEL/!_KERNEL */ sbuf_printf(sb, "\n"); } /* * If the sense data is a physical pointer, forget it. */ if (csio->ccb_h.flags & CAM_SENSE_PTR) { if (csio->ccb_h.flags & CAM_SENSE_PHYS) { #ifdef _KERNEL xpt_free_ccb((union ccb*)cgd); #endif /* _KERNEL/!_KERNEL */ return(-1); } else { /* * bcopy the pointer to avoid unaligned access * errors on finicky architectures. We don't * ensure that the sense data is pointer aligned. */ bcopy((struct scsi_sense_data **)&csio->sense_data, &sense, sizeof(struct scsi_sense_data *)); } } else { /* * If the physical sense flag is set, but the sense pointer * is not also set, we assume that the user is an idiot and * return. (Well, okay, it could be that somehow, the * entire csio is physical, but we would have probably core * dumped on one of the bogus pointer deferences above * already.) */ if (csio->ccb_h.flags & CAM_SENSE_PHYS) { #ifdef _KERNEL xpt_free_ccb((union ccb*)cgd); #endif /* _KERNEL/!_KERNEL */ return(-1); } else sense = &csio->sense_data; } scsi_sense_only_sbuf(sense, csio->sense_len - csio->sense_resid, sb, path_str, inq_data, scsiio_cdb_ptr(csio), csio->cdb_len); #ifdef _KERNEL xpt_free_ccb((union ccb*)cgd); #endif /* _KERNEL/!_KERNEL */ return(0); } #ifdef _KERNEL char * scsi_sense_string(struct ccb_scsiio *csio, char *str, int str_len) #else /* !_KERNEL */ char * scsi_sense_string(struct cam_device *device, struct ccb_scsiio *csio, char *str, int str_len) #endif /* _KERNEL/!_KERNEL */ { struct sbuf sb; sbuf_new(&sb, str, str_len, 0); #ifdef _KERNEL scsi_sense_sbuf(csio, &sb, SSS_FLAG_PRINT_COMMAND); #else /* !_KERNEL */ scsi_sense_sbuf(device, csio, &sb, SSS_FLAG_PRINT_COMMAND); #endif /* _KERNEL/!_KERNEL */ sbuf_finish(&sb); return(sbuf_data(&sb)); } #ifdef _KERNEL void scsi_sense_print(struct ccb_scsiio *csio) { struct sbuf sb; char str[512]; sbuf_new(&sb, str, sizeof(str), 0); scsi_sense_sbuf(csio, &sb, SSS_FLAG_PRINT_COMMAND); sbuf_finish(&sb); sbuf_putbuf(&sb); } #else /* !_KERNEL */ void scsi_sense_print(struct cam_device *device, struct ccb_scsiio *csio, FILE *ofile) { struct sbuf sb; char str[512]; if ((device == NULL) || (csio == NULL) || (ofile == NULL)) return; sbuf_new(&sb, str, sizeof(str), 0); scsi_sense_sbuf(device, csio, &sb, SSS_FLAG_PRINT_COMMAND); sbuf_finish(&sb); fprintf(ofile, "%s", sbuf_data(&sb)); } #endif /* _KERNEL/!_KERNEL */ /* * Extract basic sense information. This is backward-compatible with the * previous implementation. For new implementations, * scsi_extract_sense_len() is recommended. */ void scsi_extract_sense(struct scsi_sense_data *sense_data, int *error_code, int *sense_key, int *asc, int *ascq) { scsi_extract_sense_len(sense_data, sizeof(*sense_data), error_code, sense_key, asc, ascq, /*show_errors*/ 0); } /* * Extract basic sense information from SCSI I/O CCB structure. */ int scsi_extract_sense_ccb(union ccb *ccb, int *error_code, int *sense_key, int *asc, int *ascq) { struct scsi_sense_data *sense_data; /* Make sure there are some sense data we can access. */ if (ccb->ccb_h.func_code != XPT_SCSI_IO || (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR || (ccb->csio.scsi_status != SCSI_STATUS_CHECK_COND) || (ccb->ccb_h.status & CAM_AUTOSNS_VALID) == 0 || (ccb->ccb_h.flags & CAM_SENSE_PHYS)) return (0); if (ccb->ccb_h.flags & CAM_SENSE_PTR) bcopy((struct scsi_sense_data **)&ccb->csio.sense_data, &sense_data, sizeof(struct scsi_sense_data *)); else sense_data = &ccb->csio.sense_data; scsi_extract_sense_len(sense_data, ccb->csio.sense_len - ccb->csio.sense_resid, error_code, sense_key, asc, ascq, 1); if (*error_code == -1) return (0); return (1); } /* * Extract basic sense information. If show_errors is set, sense values * will be set to -1 if they are not present. */ void scsi_extract_sense_len(struct scsi_sense_data *sense_data, u_int sense_len, int *error_code, int *sense_key, int *asc, int *ascq, int show_errors) { /* * If we have no length, we have no sense. */ if (sense_len == 0) { if (show_errors == 0) { *error_code = 0; *sense_key = 0; *asc = 0; *ascq = 0; } else { *error_code = -1; *sense_key = -1; *asc = -1; *ascq = -1; } return; } *error_code = sense_data->error_code & SSD_ERRCODE; switch (*error_code) { case SSD_DESC_CURRENT_ERROR: case SSD_DESC_DEFERRED_ERROR: { struct scsi_sense_data_desc *sense; sense = (struct scsi_sense_data_desc *)sense_data; if (SSD_DESC_IS_PRESENT(sense, sense_len, sense_key)) *sense_key = sense->sense_key & SSD_KEY; else *sense_key = (show_errors) ? -1 : 0; if (SSD_DESC_IS_PRESENT(sense, sense_len, add_sense_code)) *asc = sense->add_sense_code; else *asc = (show_errors) ? -1 : 0; if (SSD_DESC_IS_PRESENT(sense, sense_len, add_sense_code_qual)) *ascq = sense->add_sense_code_qual; else *ascq = (show_errors) ? -1 : 0; break; } case SSD_CURRENT_ERROR: case SSD_DEFERRED_ERROR: default: { struct scsi_sense_data_fixed *sense; sense = (struct scsi_sense_data_fixed *)sense_data; if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags)) *sense_key = sense->flags & SSD_KEY; else *sense_key = (show_errors) ? -1 : 0; if ((SSD_FIXED_IS_PRESENT(sense, sense_len, add_sense_code)) && (SSD_FIXED_IS_FILLED(sense, add_sense_code))) *asc = sense->add_sense_code; else *asc = (show_errors) ? -1 : 0; if ((SSD_FIXED_IS_PRESENT(sense, sense_len,add_sense_code_qual)) && (SSD_FIXED_IS_FILLED(sense, add_sense_code_qual))) *ascq = sense->add_sense_code_qual; else *ascq = (show_errors) ? -1 : 0; break; } } } int scsi_get_sense_key(struct scsi_sense_data *sense_data, u_int sense_len, int show_errors) { int error_code, sense_key, asc, ascq; scsi_extract_sense_len(sense_data, sense_len, &error_code, &sense_key, &asc, &ascq, show_errors); return (sense_key); } int scsi_get_asc(struct scsi_sense_data *sense_data, u_int sense_len, int show_errors) { int error_code, sense_key, asc, ascq; scsi_extract_sense_len(sense_data, sense_len, &error_code, &sense_key, &asc, &ascq, show_errors); return (asc); } int scsi_get_ascq(struct scsi_sense_data *sense_data, u_int sense_len, int show_errors) { int error_code, sense_key, asc, ascq; scsi_extract_sense_len(sense_data, sense_len, &error_code, &sense_key, &asc, &ascq, show_errors); return (ascq); } /* * This function currently requires at least 36 bytes, or * SHORT_INQUIRY_LENGTH, worth of data to function properly. If this * function needs more or less data in the future, another length should be * defined in scsi_all.h to indicate the minimum amount of data necessary * for this routine to function properly. */ void scsi_print_inquiry_sbuf(struct sbuf *sb, struct scsi_inquiry_data *inq_data) { u_int8_t type; char *dtype, *qtype; type = SID_TYPE(inq_data); /* * Figure out basic device type and qualifier. */ if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) { qtype = " (vendor-unique qualifier)"; } else { switch (SID_QUAL(inq_data)) { case SID_QUAL_LU_CONNECTED: qtype = ""; break; case SID_QUAL_LU_OFFLINE: qtype = " (offline)"; break; case SID_QUAL_RSVD: qtype = " (reserved qualifier)"; break; default: case SID_QUAL_BAD_LU: qtype = " (LUN not supported)"; break; } } switch (type) { case T_DIRECT: dtype = "Direct Access"; break; case T_SEQUENTIAL: dtype = "Sequential Access"; break; case T_PRINTER: dtype = "Printer"; break; case T_PROCESSOR: dtype = "Processor"; break; case T_WORM: dtype = "WORM"; break; case T_CDROM: dtype = "CD-ROM"; break; case T_SCANNER: dtype = "Scanner"; break; case T_OPTICAL: dtype = "Optical"; break; case T_CHANGER: dtype = "Changer"; break; case T_COMM: dtype = "Communication"; break; case T_STORARRAY: dtype = "Storage Array"; break; case T_ENCLOSURE: dtype = "Enclosure Services"; break; case T_RBC: dtype = "Simplified Direct Access"; break; case T_OCRW: dtype = "Optical Card Read/Write"; break; case T_OSD: dtype = "Object-Based Storage"; break; case T_ADC: dtype = "Automation/Drive Interface"; break; case T_ZBC_HM: dtype = "Host Managed Zoned Block"; break; case T_NODEVICE: dtype = "Uninstalled"; break; default: dtype = "unknown"; break; } scsi_print_inquiry_short_sbuf(sb, inq_data); sbuf_printf(sb, "%s %s ", SID_IS_REMOVABLE(inq_data) ? "Removable" : "Fixed", dtype); if (SID_ANSI_REV(inq_data) == SCSI_REV_0) sbuf_printf(sb, "SCSI "); else if (SID_ANSI_REV(inq_data) <= SCSI_REV_SPC) { sbuf_printf(sb, "SCSI-%d ", SID_ANSI_REV(inq_data)); } else { sbuf_printf(sb, "SPC-%d SCSI ", SID_ANSI_REV(inq_data) - 2); } sbuf_printf(sb, "device%s\n", qtype); } void scsi_print_inquiry(struct scsi_inquiry_data *inq_data) { struct sbuf sb; char buffer[120]; sbuf_new(&sb, buffer, 120, SBUF_FIXEDLEN); scsi_print_inquiry_sbuf(&sb, inq_data); sbuf_finish(&sb); sbuf_putbuf(&sb); } void scsi_print_inquiry_short_sbuf(struct sbuf *sb, struct scsi_inquiry_data *inq_data) { sbuf_printf(sb, "<"); cam_strvis_sbuf(sb, inq_data->vendor, sizeof(inq_data->vendor), 0); sbuf_printf(sb, " "); cam_strvis_sbuf(sb, inq_data->product, sizeof(inq_data->product), 0); sbuf_printf(sb, " "); cam_strvis_sbuf(sb, inq_data->revision, sizeof(inq_data->revision), 0); sbuf_printf(sb, "> "); } void scsi_print_inquiry_short(struct scsi_inquiry_data *inq_data) { struct sbuf sb; char buffer[84]; sbuf_new(&sb, buffer, 84, SBUF_FIXEDLEN); scsi_print_inquiry_short_sbuf(&sb, inq_data); sbuf_finish(&sb); sbuf_putbuf(&sb); } /* * Table of syncrates that don't follow the "divisible by 4" * rule. This table will be expanded in future SCSI specs. */ static struct { u_int period_factor; u_int period; /* in 100ths of ns */ } scsi_syncrates[] = { { 0x08, 625 }, /* FAST-160 */ { 0x09, 1250 }, /* FAST-80 */ { 0x0a, 2500 }, /* FAST-40 40MHz */ { 0x0b, 3030 }, /* FAST-40 33MHz */ { 0x0c, 5000 } /* FAST-20 */ }; /* * Return the frequency in kHz corresponding to the given * sync period factor. */ u_int scsi_calc_syncsrate(u_int period_factor) { u_int i; u_int num_syncrates; /* * It's a bug if period is zero, but if it is anyway, don't * die with a divide fault- instead return something which * 'approximates' async */ if (period_factor == 0) { return (3300); } num_syncrates = nitems(scsi_syncrates); /* See if the period is in the "exception" table */ for (i = 0; i < num_syncrates; i++) { if (period_factor == scsi_syncrates[i].period_factor) { /* Period in kHz */ return (100000000 / scsi_syncrates[i].period); } } /* * Wasn't in the table, so use the standard * 4 times conversion. */ return (10000000 / (period_factor * 4 * 10)); } /* * Return the SCSI sync parameter that corresponds to * the passed in period in 10ths of ns. */ u_int scsi_calc_syncparam(u_int period) { u_int i; u_int num_syncrates; if (period == 0) return (~0); /* Async */ /* Adjust for exception table being in 100ths. */ period *= 10; num_syncrates = nitems(scsi_syncrates); /* See if the period is in the "exception" table */ for (i = 0; i < num_syncrates; i++) { if (period <= scsi_syncrates[i].period) { /* Period in 100ths of ns */ return (scsi_syncrates[i].period_factor); } } /* * Wasn't in the table, so use the standard * 1/4 period in ns conversion. */ return (period/400); } int scsi_devid_is_naa_ieee_reg(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; struct scsi_vpd_id_naa_basic *naa; descr = (struct scsi_vpd_id_descriptor *)bufp; naa = (struct scsi_vpd_id_naa_basic *)descr->identifier; if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_NAA) return 0; if (descr->length < sizeof(struct scsi_vpd_id_naa_ieee_reg)) return 0; if ((naa->naa >> SVPD_ID_NAA_NAA_SHIFT) != SVPD_ID_NAA_IEEE_REG) return 0; return 1; } int scsi_devid_is_sas_target(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; descr = (struct scsi_vpd_id_descriptor *)bufp; if (!scsi_devid_is_naa_ieee_reg(bufp)) return 0; if ((descr->id_type & SVPD_ID_PIV) == 0) /* proto field reserved */ return 0; if ((descr->proto_codeset >> SVPD_ID_PROTO_SHIFT) != SCSI_PROTO_SAS) return 0; return 1; } int scsi_devid_is_lun_eui64(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; descr = (struct scsi_vpd_id_descriptor *)bufp; if ((descr->id_type & SVPD_ID_ASSOC_MASK) != SVPD_ID_ASSOC_LUN) return 0; if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_EUI64) return 0; return 1; } int scsi_devid_is_lun_naa(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; descr = (struct scsi_vpd_id_descriptor *)bufp; if ((descr->id_type & SVPD_ID_ASSOC_MASK) != SVPD_ID_ASSOC_LUN) return 0; if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_NAA) return 0; return 1; } int scsi_devid_is_lun_t10(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; descr = (struct scsi_vpd_id_descriptor *)bufp; if ((descr->id_type & SVPD_ID_ASSOC_MASK) != SVPD_ID_ASSOC_LUN) return 0; if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_T10) return 0; return 1; } int scsi_devid_is_lun_name(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; descr = (struct scsi_vpd_id_descriptor *)bufp; if ((descr->id_type & SVPD_ID_ASSOC_MASK) != SVPD_ID_ASSOC_LUN) return 0; if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_SCSI_NAME) return 0; return 1; } int scsi_devid_is_lun_md5(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; descr = (struct scsi_vpd_id_descriptor *)bufp; if ((descr->id_type & SVPD_ID_ASSOC_MASK) != SVPD_ID_ASSOC_LUN) return 0; if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_MD5_LUN_ID) return 0; return 1; } int scsi_devid_is_lun_uuid(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; descr = (struct scsi_vpd_id_descriptor *)bufp; if ((descr->id_type & SVPD_ID_ASSOC_MASK) != SVPD_ID_ASSOC_LUN) return 0; if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_UUID) return 0; return 1; } int scsi_devid_is_port_naa(uint8_t *bufp) { struct scsi_vpd_id_descriptor *descr; descr = (struct scsi_vpd_id_descriptor *)bufp; if ((descr->id_type & SVPD_ID_ASSOC_MASK) != SVPD_ID_ASSOC_PORT) return 0; if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_NAA) return 0; return 1; } struct scsi_vpd_id_descriptor * scsi_get_devid_desc(struct scsi_vpd_id_descriptor *desc, uint32_t len, scsi_devid_checkfn_t ck_fn) { uint8_t *desc_buf_end; desc_buf_end = (uint8_t *)desc + len; for (; desc->identifier <= desc_buf_end && desc->identifier + desc->length <= desc_buf_end; desc = (struct scsi_vpd_id_descriptor *)(desc->identifier + desc->length)) { if (ck_fn == NULL || ck_fn((uint8_t *)desc) != 0) return (desc); } return (NULL); } struct scsi_vpd_id_descriptor * scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t page_len, scsi_devid_checkfn_t ck_fn) { uint32_t len; if (page_len < sizeof(*id)) return (NULL); len = MIN(scsi_2btoul(id->length), page_len - sizeof(*id)); return (scsi_get_devid_desc((struct scsi_vpd_id_descriptor *) id->desc_list, len, ck_fn)); } int scsi_transportid_sbuf(struct sbuf *sb, struct scsi_transportid_header *hdr, uint32_t valid_len) { switch (hdr->format_protocol & SCSI_TRN_PROTO_MASK) { case SCSI_PROTO_FC: { struct scsi_transportid_fcp *fcp; uint64_t n_port_name; fcp = (struct scsi_transportid_fcp *)hdr; n_port_name = scsi_8btou64(fcp->n_port_name); sbuf_printf(sb, "FCP address: 0x%.16jx",(uintmax_t)n_port_name); break; } case SCSI_PROTO_SPI: { struct scsi_transportid_spi *spi; spi = (struct scsi_transportid_spi *)hdr; sbuf_printf(sb, "SPI address: %u,%u", scsi_2btoul(spi->scsi_addr), scsi_2btoul(spi->rel_trgt_port_id)); break; } case SCSI_PROTO_SSA: /* * XXX KDM there is no transport ID defined in SPC-4 for * SSA. */ break; case SCSI_PROTO_1394: { struct scsi_transportid_1394 *sbp; uint64_t eui64; sbp = (struct scsi_transportid_1394 *)hdr; eui64 = scsi_8btou64(sbp->eui64); sbuf_printf(sb, "SBP address: 0x%.16jx", (uintmax_t)eui64); break; } case SCSI_PROTO_RDMA: { struct scsi_transportid_rdma *rdma; unsigned int i; rdma = (struct scsi_transportid_rdma *)hdr; sbuf_printf(sb, "RDMA address: 0x"); for (i = 0; i < sizeof(rdma->initiator_port_id); i++) sbuf_printf(sb, "%02x", rdma->initiator_port_id[i]); break; } case SCSI_PROTO_ISCSI: { uint32_t add_len, i; uint8_t *iscsi_name = NULL; int nul_found = 0; sbuf_printf(sb, "iSCSI address: "); if ((hdr->format_protocol & SCSI_TRN_FORMAT_MASK) == SCSI_TRN_ISCSI_FORMAT_DEVICE) { struct scsi_transportid_iscsi_device *dev; dev = (struct scsi_transportid_iscsi_device *)hdr; /* * Verify how much additional data we really have. */ add_len = scsi_2btoul(dev->additional_length); add_len = MIN(add_len, valid_len - __offsetof(struct scsi_transportid_iscsi_device, iscsi_name)); iscsi_name = &dev->iscsi_name[0]; } else if ((hdr->format_protocol & SCSI_TRN_FORMAT_MASK) == SCSI_TRN_ISCSI_FORMAT_PORT) { struct scsi_transportid_iscsi_port *port; port = (struct scsi_transportid_iscsi_port *)hdr; add_len = scsi_2btoul(port->additional_length); add_len = MIN(add_len, valid_len - __offsetof(struct scsi_transportid_iscsi_port, iscsi_name)); iscsi_name = &port->iscsi_name[0]; } else { sbuf_printf(sb, "unknown format %x", (hdr->format_protocol & SCSI_TRN_FORMAT_MASK) >> SCSI_TRN_FORMAT_SHIFT); break; } if (add_len == 0) { sbuf_printf(sb, "not enough data"); break; } /* * This is supposed to be a NUL-terminated ASCII * string, but you never know. So we're going to * check. We need to do this because there is no * sbuf equivalent of strncat(). */ for (i = 0; i < add_len; i++) { if (iscsi_name[i] == '\0') { nul_found = 1; break; } } /* * If there is a NUL in the name, we can just use * sbuf_cat(). Otherwise we need to use sbuf_bcat(). */ if (nul_found != 0) sbuf_cat(sb, iscsi_name); else sbuf_bcat(sb, iscsi_name, add_len); break; } case SCSI_PROTO_SAS: { struct scsi_transportid_sas *sas; uint64_t sas_addr; sas = (struct scsi_transportid_sas *)hdr; sas_addr = scsi_8btou64(sas->sas_address); sbuf_printf(sb, "SAS address: 0x%.16jx", (uintmax_t)sas_addr); break; } case SCSI_PROTO_ADITP: case SCSI_PROTO_ATA: case SCSI_PROTO_UAS: /* * No Transport ID format for ADI, ATA or USB is defined in * SPC-4. */ sbuf_printf(sb, "No known Transport ID format for protocol " "%#x", hdr->format_protocol & SCSI_TRN_PROTO_MASK); break; case SCSI_PROTO_SOP: { struct scsi_transportid_sop *sop; struct scsi_sop_routing_id_norm *rid; sop = (struct scsi_transportid_sop *)hdr; rid = (struct scsi_sop_routing_id_norm *)sop->routing_id; /* * Note that there is no alternate format specified in SPC-4 * for the PCIe routing ID, so we don't really have a way * to know whether the second byte of the routing ID is * a device and function or just a function. So we just * assume bus,device,function. */ sbuf_printf(sb, "SOP Routing ID: %u,%u,%u", rid->bus, rid->devfunc >> SCSI_TRN_SOP_DEV_SHIFT, rid->devfunc & SCSI_TRN_SOP_FUNC_NORM_MAX); break; } case SCSI_PROTO_NONE: default: sbuf_printf(sb, "Unknown protocol %#x", hdr->format_protocol & SCSI_TRN_PROTO_MASK); break; } return (0); } struct scsi_nv scsi_proto_map[] = { { "fcp", SCSI_PROTO_FC }, { "spi", SCSI_PROTO_SPI }, { "ssa", SCSI_PROTO_SSA }, { "sbp", SCSI_PROTO_1394 }, { "1394", SCSI_PROTO_1394 }, { "srp", SCSI_PROTO_RDMA }, { "rdma", SCSI_PROTO_RDMA }, { "iscsi", SCSI_PROTO_ISCSI }, { "iqn", SCSI_PROTO_ISCSI }, { "sas", SCSI_PROTO_SAS }, { "aditp", SCSI_PROTO_ADITP }, { "ata", SCSI_PROTO_ATA }, { "uas", SCSI_PROTO_UAS }, { "usb", SCSI_PROTO_UAS }, { "sop", SCSI_PROTO_SOP } }; const char * scsi_nv_to_str(struct scsi_nv *table, int num_table_entries, uint64_t value) { int i; for (i = 0; i < num_table_entries; i++) { if (table[i].value == value) return (table[i].name); } return (NULL); } /* * Given a name/value table, find a value matching the given name. * Return values: * SCSI_NV_FOUND - match found * SCSI_NV_AMBIGUOUS - more than one match, none of them exact * SCSI_NV_NOT_FOUND - no match found */ scsi_nv_status scsi_get_nv(struct scsi_nv *table, int num_table_entries, char *name, int *table_entry, scsi_nv_flags flags) { int i, num_matches = 0; for (i = 0; i < num_table_entries; i++) { size_t table_len, name_len; table_len = strlen(table[i].name); name_len = strlen(name); if ((((flags & SCSI_NV_FLAG_IG_CASE) != 0) && (strncasecmp(table[i].name, name, name_len) == 0)) || (((flags & SCSI_NV_FLAG_IG_CASE) == 0) && (strncmp(table[i].name, name, name_len) == 0))) { *table_entry = i; /* * Check for an exact match. If we have the same * number of characters in the table as the argument, * and we already know they're the same, we have * an exact match. */ if (table_len == name_len) return (SCSI_NV_FOUND); /* * Otherwise, bump up the number of matches. We'll * see later how many we have. */ num_matches++; } } if (num_matches > 1) return (SCSI_NV_AMBIGUOUS); else if (num_matches == 1) return (SCSI_NV_FOUND); else return (SCSI_NV_NOT_FOUND); } /* * Parse transport IDs for Fibre Channel, 1394 and SAS. Since these are * all 64-bit numbers, the code is similar. */ int scsi_parse_transportid_64bit(int proto_id, char *id_str, struct scsi_transportid_header **hdr, unsigned int *alloc_len, #ifdef _KERNEL struct malloc_type *type, int flags, #endif char *error_str, int error_str_len) { uint64_t value; char *endptr; int retval; size_t alloc_size; retval = 0; value = strtouq(id_str, &endptr, 0); if (*endptr != '\0') { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: error " "parsing ID %s, 64-bit number required", __func__, id_str); } retval = 1; goto bailout; } switch (proto_id) { case SCSI_PROTO_FC: alloc_size = sizeof(struct scsi_transportid_fcp); break; case SCSI_PROTO_1394: alloc_size = sizeof(struct scsi_transportid_1394); break; case SCSI_PROTO_SAS: alloc_size = sizeof(struct scsi_transportid_sas); break; default: if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: unsupported " "protocol %d", __func__, proto_id); } retval = 1; goto bailout; break; /* NOTREACHED */ } #ifdef _KERNEL *hdr = malloc(alloc_size, type, flags); #else /* _KERNEL */ *hdr = malloc(alloc_size); #endif /*_KERNEL */ if (*hdr == NULL) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: unable to " "allocate %zu bytes", __func__, alloc_size); } retval = 1; goto bailout; } *alloc_len = alloc_size; bzero(*hdr, alloc_size); switch (proto_id) { case SCSI_PROTO_FC: { struct scsi_transportid_fcp *fcp; fcp = (struct scsi_transportid_fcp *)(*hdr); fcp->format_protocol = SCSI_PROTO_FC | SCSI_TRN_FCP_FORMAT_DEFAULT; scsi_u64to8b(value, fcp->n_port_name); break; } case SCSI_PROTO_1394: { struct scsi_transportid_1394 *sbp; sbp = (struct scsi_transportid_1394 *)(*hdr); sbp->format_protocol = SCSI_PROTO_1394 | SCSI_TRN_1394_FORMAT_DEFAULT; scsi_u64to8b(value, sbp->eui64); break; } case SCSI_PROTO_SAS: { struct scsi_transportid_sas *sas; sas = (struct scsi_transportid_sas *)(*hdr); sas->format_protocol = SCSI_PROTO_SAS | SCSI_TRN_SAS_FORMAT_DEFAULT; scsi_u64to8b(value, sas->sas_address); break; } default: break; } bailout: return (retval); } /* * Parse a SPI (Parallel SCSI) address of the form: id,rel_tgt_port */ int scsi_parse_transportid_spi(char *id_str, struct scsi_transportid_header **hdr, unsigned int *alloc_len, #ifdef _KERNEL struct malloc_type *type, int flags, #endif char *error_str, int error_str_len) { unsigned long scsi_addr, target_port; struct scsi_transportid_spi *spi; char *tmpstr, *endptr; int retval; retval = 0; tmpstr = strsep(&id_str, ","); if (tmpstr == NULL) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: no ID found", __func__); } retval = 1; goto bailout; } scsi_addr = strtoul(tmpstr, &endptr, 0); if (*endptr != '\0') { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: error " "parsing SCSI ID %s, number required", __func__, tmpstr); } retval = 1; goto bailout; } if (id_str == NULL) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: no relative " "target port found", __func__); } retval = 1; goto bailout; } target_port = strtoul(id_str, &endptr, 0); if (*endptr != '\0') { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: error " "parsing relative target port %s, number " "required", __func__, id_str); } retval = 1; goto bailout; } #ifdef _KERNEL spi = malloc(sizeof(*spi), type, flags); #else spi = malloc(sizeof(*spi)); #endif if (spi == NULL) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: unable to " "allocate %zu bytes", __func__, sizeof(*spi)); } retval = 1; goto bailout; } *alloc_len = sizeof(*spi); bzero(spi, sizeof(*spi)); spi->format_protocol = SCSI_PROTO_SPI | SCSI_TRN_SPI_FORMAT_DEFAULT; scsi_ulto2b(scsi_addr, spi->scsi_addr); scsi_ulto2b(target_port, spi->rel_trgt_port_id); *hdr = (struct scsi_transportid_header *)spi; bailout: return (retval); } /* * Parse an RDMA/SRP Initiator Port ID string. This is 32 hexadecimal digits, * optionally prefixed by "0x" or "0X". */ int scsi_parse_transportid_rdma(char *id_str, struct scsi_transportid_header **hdr, unsigned int *alloc_len, #ifdef _KERNEL struct malloc_type *type, int flags, #endif char *error_str, int error_str_len) { struct scsi_transportid_rdma *rdma; int retval; size_t id_len, rdma_id_size; uint8_t rdma_id[SCSI_TRN_RDMA_PORT_LEN]; char *tmpstr; unsigned int i, j; retval = 0; id_len = strlen(id_str); rdma_id_size = SCSI_TRN_RDMA_PORT_LEN; /* * Check the size. It needs to be either 32 or 34 characters long. */ if ((id_len != (rdma_id_size * 2)) && (id_len != ((rdma_id_size * 2) + 2))) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: RDMA ID " "must be 32 hex digits (0x prefix " "optional), only %zu seen", __func__, id_len); } retval = 1; goto bailout; } tmpstr = id_str; /* * If the user gave us 34 characters, the string needs to start * with '0x'. */ if (id_len == ((rdma_id_size * 2) + 2)) { if ((tmpstr[0] == '0') && ((tmpstr[1] == 'x') || (tmpstr[1] == 'X'))) { tmpstr += 2; } else { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: RDMA " "ID prefix, if used, must be \"0x\", " "got %s", __func__, tmpstr); } retval = 1; goto bailout; } } bzero(rdma_id, sizeof(rdma_id)); /* * Convert ASCII hex into binary bytes. There is no standard * 128-bit integer type, and so no strtou128t() routine to convert * from hex into a large integer. In the end, we're not going to * an integer, but rather to a byte array, so that and the fact * that we require the user to give us 32 hex digits simplifies the * logic. */ for (i = 0; i < (rdma_id_size * 2); i++) { int cur_shift; unsigned char c; /* Increment the byte array one for every 2 hex digits */ j = i >> 1; /* * The first digit in every pair is the most significant * 4 bits. The second is the least significant 4 bits. */ if ((i % 2) == 0) cur_shift = 4; else cur_shift = 0; c = tmpstr[i]; /* Convert the ASCII hex character into a number */ if (isdigit(c)) c -= '0'; else if (isalpha(c)) c -= isupper(c) ? 'A' - 10 : 'a' - 10; else { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: " "RDMA ID must be hex digits, got " "invalid character %c", __func__, tmpstr[i]); } retval = 1; goto bailout; } /* * The converted number can't be less than 0; the type is * unsigned, and the subtraction logic will not give us * a negative number. So we only need to make sure that * the value is not greater than 0xf. (i.e. make sure the * user didn't give us a value like "0x12jklmno"). */ if (c > 0xf) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: " "RDMA ID must be hex digits, got " "invalid character %c", __func__, tmpstr[i]); } retval = 1; goto bailout; } rdma_id[j] |= c << cur_shift; } #ifdef _KERNEL rdma = malloc(sizeof(*rdma), type, flags); #else rdma = malloc(sizeof(*rdma)); #endif if (rdma == NULL) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: unable to " "allocate %zu bytes", __func__, sizeof(*rdma)); } retval = 1; goto bailout; } *alloc_len = sizeof(*rdma); bzero(rdma, *alloc_len); rdma->format_protocol = SCSI_PROTO_RDMA | SCSI_TRN_RDMA_FORMAT_DEFAULT; bcopy(rdma_id, rdma->initiator_port_id, SCSI_TRN_RDMA_PORT_LEN); *hdr = (struct scsi_transportid_header *)rdma; bailout: return (retval); } /* * Parse an iSCSI name. The format is either just the name: * * iqn.2012-06.com.example:target0 * or the name, separator and initiator session ID: * * iqn.2012-06.com.example:target0,i,0x123 * * The separator format is exact. */ int scsi_parse_transportid_iscsi(char *id_str, struct scsi_transportid_header **hdr, unsigned int *alloc_len, #ifdef _KERNEL struct malloc_type *type, int flags, #endif char *error_str, int error_str_len) { size_t id_len, sep_len, id_size, name_len; int retval; unsigned int i, sep_pos, sep_found; const char *sep_template = ",i,0x"; const char *iqn_prefix = "iqn."; struct scsi_transportid_iscsi_device *iscsi; retval = 0; sep_found = 0; id_len = strlen(id_str); sep_len = strlen(sep_template); /* * The separator is defined as exactly ',i,0x'. Any other commas, * or any other form, is an error. So look for a comma, and once * we find that, the next few characters must match the separator * exactly. Once we get through the separator, there should be at * least one character. */ for (i = 0, sep_pos = 0; i < id_len; i++) { if (sep_pos == 0) { if (id_str[i] == sep_template[sep_pos]) sep_pos++; continue; } if (sep_pos < sep_len) { if (id_str[i] == sep_template[sep_pos]) { sep_pos++; continue; } if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: " "invalid separator in iSCSI name " "\"%s\"", __func__, id_str); } retval = 1; goto bailout; } else { sep_found = 1; break; } } /* * Check to see whether we have a separator but no digits after it. */ if ((sep_pos != 0) && (sep_found == 0)) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: no digits " "found after separator in iSCSI name \"%s\"", __func__, id_str); } retval = 1; goto bailout; } /* * The incoming ID string has the "iqn." prefix stripped off. We * need enough space for the base structure (the structures are the * same for the two iSCSI forms), the prefix, the ID string and a * terminating NUL. */ id_size = sizeof(*iscsi) + strlen(iqn_prefix) + id_len + 1; #ifdef _KERNEL iscsi = malloc(id_size, type, flags); #else iscsi = malloc(id_size); #endif if (iscsi == NULL) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: unable to " "allocate %zu bytes", __func__, id_size); } retval = 1; goto bailout; } *alloc_len = id_size; bzero(iscsi, id_size); iscsi->format_protocol = SCSI_PROTO_ISCSI; if (sep_found == 0) iscsi->format_protocol |= SCSI_TRN_ISCSI_FORMAT_DEVICE; else iscsi->format_protocol |= SCSI_TRN_ISCSI_FORMAT_PORT; name_len = id_size - sizeof(*iscsi); scsi_ulto2b(name_len, iscsi->additional_length); snprintf(iscsi->iscsi_name, name_len, "%s%s", iqn_prefix, id_str); *hdr = (struct scsi_transportid_header *)iscsi; bailout: return (retval); } /* * Parse a SCSI over PCIe (SOP) identifier. The Routing ID can either be * of the form 'bus,device,function' or 'bus,function'. */ int scsi_parse_transportid_sop(char *id_str, struct scsi_transportid_header **hdr, unsigned int *alloc_len, #ifdef _KERNEL struct malloc_type *type, int flags, #endif char *error_str, int error_str_len) { struct scsi_transportid_sop *sop; unsigned long bus, device, function; char *tmpstr, *endptr; int retval, device_spec; retval = 0; device_spec = 0; device = 0; tmpstr = strsep(&id_str, ","); if ((tmpstr == NULL) || (*tmpstr == '\0')) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: no ID found", __func__); } retval = 1; goto bailout; } bus = strtoul(tmpstr, &endptr, 0); if (*endptr != '\0') { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: error " "parsing PCIe bus %s, number required", __func__, tmpstr); } retval = 1; goto bailout; } if ((id_str == NULL) || (*id_str == '\0')) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: no PCIe " "device or function found", __func__); } retval = 1; goto bailout; } tmpstr = strsep(&id_str, ","); function = strtoul(tmpstr, &endptr, 0); if (*endptr != '\0') { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: error " "parsing PCIe device/function %s, number " "required", __func__, tmpstr); } retval = 1; goto bailout; } /* * Check to see whether the user specified a third value. If so, * the second is the device. */ if (id_str != NULL) { if (*id_str == '\0') { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: " "no PCIe function found", __func__); } retval = 1; goto bailout; } device = function; device_spec = 1; function = strtoul(id_str, &endptr, 0); if (*endptr != '\0') { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: " "error parsing PCIe function %s, " "number required", __func__, id_str); } retval = 1; goto bailout; } } if (bus > SCSI_TRN_SOP_BUS_MAX) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: bus value " "%lu greater than maximum %u", __func__, bus, SCSI_TRN_SOP_BUS_MAX); } retval = 1; goto bailout; } if ((device_spec != 0) && (device > SCSI_TRN_SOP_DEV_MASK)) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: device value " "%lu greater than maximum %u", __func__, device, SCSI_TRN_SOP_DEV_MAX); } retval = 1; goto bailout; } if (((device_spec != 0) && (function > SCSI_TRN_SOP_FUNC_NORM_MAX)) || ((device_spec == 0) && (function > SCSI_TRN_SOP_FUNC_ALT_MAX))) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: function value " "%lu greater than maximum %u", __func__, function, (device_spec == 0) ? SCSI_TRN_SOP_FUNC_ALT_MAX : SCSI_TRN_SOP_FUNC_NORM_MAX); } retval = 1; goto bailout; } #ifdef _KERNEL sop = malloc(sizeof(*sop), type, flags); #else sop = malloc(sizeof(*sop)); #endif if (sop == NULL) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: unable to " "allocate %zu bytes", __func__, sizeof(*sop)); } retval = 1; goto bailout; } *alloc_len = sizeof(*sop); bzero(sop, sizeof(*sop)); sop->format_protocol = SCSI_PROTO_SOP | SCSI_TRN_SOP_FORMAT_DEFAULT; if (device_spec != 0) { struct scsi_sop_routing_id_norm rid; rid.bus = bus; rid.devfunc = (device << SCSI_TRN_SOP_DEV_SHIFT) | function; bcopy(&rid, sop->routing_id, MIN(sizeof(rid), sizeof(sop->routing_id))); } else { struct scsi_sop_routing_id_alt rid; rid.bus = bus; rid.function = function; bcopy(&rid, sop->routing_id, MIN(sizeof(rid), sizeof(sop->routing_id))); } *hdr = (struct scsi_transportid_header *)sop; bailout: return (retval); } /* * transportid_str: NUL-terminated string with format: protcol,id * The ID is protocol specific. * hdr: Storage will be allocated for the transport ID. * alloc_len: The amount of memory allocated is returned here. * type: Malloc bucket (kernel only). * flags: Malloc flags (kernel only). * error_str: If non-NULL, it will contain error information (without * a terminating newline) if an error is returned. * error_str_len: Allocated length of the error string. * * Returns 0 for success, non-zero for failure. */ int scsi_parse_transportid(char *transportid_str, struct scsi_transportid_header **hdr, unsigned int *alloc_len, #ifdef _KERNEL struct malloc_type *type, int flags, #endif char *error_str, int error_str_len) { char *tmpstr; scsi_nv_status status; u_int num_proto_entries; int retval, table_entry; retval = 0; table_entry = 0; /* * We do allow a period as well as a comma to separate the protocol * from the ID string. This is to accommodate iSCSI names, which * start with "iqn.". */ tmpstr = strsep(&transportid_str, ",."); if (tmpstr == NULL) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: transportid_str is NULL", __func__); } retval = 1; goto bailout; } num_proto_entries = nitems(scsi_proto_map); status = scsi_get_nv(scsi_proto_map, num_proto_entries, tmpstr, &table_entry, SCSI_NV_FLAG_IG_CASE); if (status != SCSI_NV_FOUND) { if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: %s protocol " "name %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", tmpstr); } retval = 1; goto bailout; } switch (scsi_proto_map[table_entry].value) { case SCSI_PROTO_FC: case SCSI_PROTO_1394: case SCSI_PROTO_SAS: retval = scsi_parse_transportid_64bit( scsi_proto_map[table_entry].value, transportid_str, hdr, alloc_len, #ifdef _KERNEL type, flags, #endif error_str, error_str_len); break; case SCSI_PROTO_SPI: retval = scsi_parse_transportid_spi(transportid_str, hdr, alloc_len, #ifdef _KERNEL type, flags, #endif error_str, error_str_len); break; case SCSI_PROTO_RDMA: retval = scsi_parse_transportid_rdma(transportid_str, hdr, alloc_len, #ifdef _KERNEL type, flags, #endif error_str, error_str_len); break; case SCSI_PROTO_ISCSI: retval = scsi_parse_transportid_iscsi(transportid_str, hdr, alloc_len, #ifdef _KERNEL type, flags, #endif error_str, error_str_len); break; case SCSI_PROTO_SOP: retval = scsi_parse_transportid_sop(transportid_str, hdr, alloc_len, #ifdef _KERNEL type, flags, #endif error_str, error_str_len); break; case SCSI_PROTO_SSA: case SCSI_PROTO_ADITP: case SCSI_PROTO_ATA: case SCSI_PROTO_UAS: case SCSI_PROTO_NONE: default: /* * There is no format defined for a Transport ID for these * protocols. So even if the user gives us something, we * have no way to turn it into a standard SCSI Transport ID. */ retval = 1; if (error_str != NULL) { snprintf(error_str, error_str_len, "%s: no Transport " "ID format exists for protocol %s", __func__, tmpstr); } goto bailout; break; /* NOTREACHED */ } bailout: return (retval); } struct scsi_attrib_table_entry scsi_mam_attr_table[] = { { SMA_ATTR_REM_CAP_PARTITION, SCSI_ATTR_FLAG_NONE, "Remaining Capacity in Partition", /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf,/*parse_str*/ NULL }, { SMA_ATTR_MAX_CAP_PARTITION, SCSI_ATTR_FLAG_NONE, "Maximum Capacity in Partition", /*suffix*/"MB", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_TAPEALERT_FLAGS, SCSI_ATTR_FLAG_HEX, "TapeAlert Flags", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_LOAD_COUNT, SCSI_ATTR_FLAG_NONE, "Load Count", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MAM_SPACE_REMAINING, SCSI_ATTR_FLAG_NONE, "MAM Space Remaining", /*suffix*/"bytes", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_DEV_ASSIGNING_ORG, SCSI_ATTR_FLAG_NONE, "Assigning Organization", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_FORMAT_DENSITY_CODE, SCSI_ATTR_FLAG_HEX, "Format Density Code", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_INITIALIZATION_COUNT, SCSI_ATTR_FLAG_NONE, "Initialization Count", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_VOLUME_ID, SCSI_ATTR_FLAG_NONE, "Volume Identifier", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_VOLUME_CHANGE_REF, SCSI_ATTR_FLAG_HEX, "Volume Change Reference", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_DEV_SERIAL_LAST_LOAD, SCSI_ATTR_FLAG_NONE, "Device Vendor/Serial at Last Load", /*suffix*/NULL, /*to_str*/ scsi_attrib_vendser_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_DEV_SERIAL_LAST_LOAD_1, SCSI_ATTR_FLAG_NONE, "Device Vendor/Serial at Last Load - 1", /*suffix*/NULL, /*to_str*/ scsi_attrib_vendser_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_DEV_SERIAL_LAST_LOAD_2, SCSI_ATTR_FLAG_NONE, "Device Vendor/Serial at Last Load - 2", /*suffix*/NULL, /*to_str*/ scsi_attrib_vendser_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_DEV_SERIAL_LAST_LOAD_3, SCSI_ATTR_FLAG_NONE, "Device Vendor/Serial at Last Load - 3", /*suffix*/NULL, /*to_str*/ scsi_attrib_vendser_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_TOTAL_MB_WRITTEN_LT, SCSI_ATTR_FLAG_NONE, "Total MB Written in Medium Life", /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_TOTAL_MB_READ_LT, SCSI_ATTR_FLAG_NONE, "Total MB Read in Medium Life", /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_TOTAL_MB_WRITTEN_CUR, SCSI_ATTR_FLAG_NONE, "Total MB Written in Current/Last Load", /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_TOTAL_MB_READ_CUR, SCSI_ATTR_FLAG_NONE, "Total MB Read in Current/Last Load", /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_FIRST_ENC_BLOCK, SCSI_ATTR_FLAG_NONE, "Logical Position of First Encrypted Block", /*suffix*/ NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_NEXT_UNENC_BLOCK, SCSI_ATTR_FLAG_NONE, "Logical Position of First Unencrypted Block after First " "Encrypted Block", /*suffix*/ NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MEDIUM_USAGE_HIST, SCSI_ATTR_FLAG_NONE, "Medium Usage History", /*suffix*/ NULL, /*to_str*/ NULL, /*parse_str*/ NULL }, { SMA_ATTR_PART_USAGE_HIST, SCSI_ATTR_FLAG_NONE, "Partition Usage History", /*suffix*/ NULL, /*to_str*/ NULL, /*parse_str*/ NULL }, { SMA_ATTR_MED_MANUF, SCSI_ATTR_FLAG_NONE, "Medium Manufacturer", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_SERIAL, SCSI_ATTR_FLAG_NONE, "Medium Serial Number", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_LENGTH, SCSI_ATTR_FLAG_NONE, "Medium Length", /*suffix*/"m", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_WIDTH, SCSI_ATTR_FLAG_FP | SCSI_ATTR_FLAG_DIV_10 | SCSI_ATTR_FLAG_FP_1DIGIT, "Medium Width", /*suffix*/"mm", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_ASSIGNING_ORG, SCSI_ATTR_FLAG_NONE, "Assigning Organization", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_DENSITY_CODE, SCSI_ATTR_FLAG_HEX, "Medium Density Code", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_MANUF_DATE, SCSI_ATTR_FLAG_NONE, "Medium Manufacture Date", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MAM_CAPACITY, SCSI_ATTR_FLAG_NONE, "MAM Capacity", /*suffix*/"bytes", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_TYPE, SCSI_ATTR_FLAG_HEX, "Medium Type", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_TYPE_INFO, SCSI_ATTR_FLAG_HEX, "Medium Type Information", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MED_SERIAL_NUM, SCSI_ATTR_FLAG_NONE, "Medium Serial Number", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_APP_VENDOR, SCSI_ATTR_FLAG_NONE, "Application Vendor", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_APP_NAME, SCSI_ATTR_FLAG_NONE, "Application Name", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_APP_VERSION, SCSI_ATTR_FLAG_NONE, "Application Version", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_USER_MED_TEXT_LABEL, SCSI_ATTR_FLAG_NONE, "User Medium Text Label", /*suffix*/NULL, /*to_str*/ scsi_attrib_text_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_LAST_WRITTEN_TIME, SCSI_ATTR_FLAG_NONE, "Date and Time Last Written", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_TEXT_LOCAL_ID, SCSI_ATTR_FLAG_HEX, "Text Localization Identifier", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_BARCODE, SCSI_ATTR_FLAG_NONE, "Barcode", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_HOST_OWNER_NAME, SCSI_ATTR_FLAG_NONE, "Owning Host Textual Name", /*suffix*/NULL, /*to_str*/ scsi_attrib_text_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_MEDIA_POOL, SCSI_ATTR_FLAG_NONE, "Media Pool", /*suffix*/NULL, /*to_str*/ scsi_attrib_text_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_PART_USER_LABEL, SCSI_ATTR_FLAG_NONE, "Partition User Text Label", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_LOAD_UNLOAD_AT_PART, SCSI_ATTR_FLAG_NONE, "Load/Unload at Partition", /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_APP_FORMAT_VERSION, SCSI_ATTR_FLAG_NONE, "Application Format Version", /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, /*parse_str*/ NULL }, { SMA_ATTR_VOL_COHERENCY_INFO, SCSI_ATTR_FLAG_NONE, "Volume Coherency Information", /*suffix*/NULL, /*to_str*/ scsi_attrib_volcoh_sbuf, /*parse_str*/ NULL }, { 0x0ff1, SCSI_ATTR_FLAG_NONE, "Spectra MLM Creation", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x0ff2, SCSI_ATTR_FLAG_NONE, "Spectra MLM C3", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x0ff3, SCSI_ATTR_FLAG_NONE, "Spectra MLM RW", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x0ff4, SCSI_ATTR_FLAG_NONE, "Spectra MLM SDC List", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x0ff7, SCSI_ATTR_FLAG_NONE, "Spectra MLM Post Scan", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x0ffe, SCSI_ATTR_FLAG_NONE, "Spectra MLM Checksum", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x17f1, SCSI_ATTR_FLAG_NONE, "Spectra MLM Creation", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x17f2, SCSI_ATTR_FLAG_NONE, "Spectra MLM C3", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x17f3, SCSI_ATTR_FLAG_NONE, "Spectra MLM RW", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x17f4, SCSI_ATTR_FLAG_NONE, "Spectra MLM SDC List", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x17f7, SCSI_ATTR_FLAG_NONE, "Spectra MLM Post Scan", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, { 0x17ff, SCSI_ATTR_FLAG_NONE, "Spectra MLM Checksum", /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, /*parse_str*/ NULL }, }; /* * Print out Volume Coherency Information (Attribute 0x080c). * This field has two variable length members, including one at the * beginning, so it isn't practical to have a fixed structure definition. * This is current as of SSC4r03 (see section 4.2.21.3), dated March 25, * 2013. */ int scsi_attrib_volcoh_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, uint32_t valid_len, uint32_t flags, uint32_t output_flags, char *error_str, int error_str_len) { size_t avail_len; uint32_t field_size; uint64_t tmp_val; uint8_t *cur_ptr; int retval; int vcr_len, as_len; retval = 0; tmp_val = 0; field_size = scsi_2btoul(hdr->length); avail_len = valid_len - sizeof(*hdr); if (field_size > avail_len) { if (error_str != NULL) { snprintf(error_str, error_str_len, "Available " "length of attribute ID 0x%.4x %zu < field " "length %u", scsi_2btoul(hdr->id), avail_len, field_size); } retval = 1; goto bailout; } else if (field_size == 0) { /* * It isn't clear from the spec whether a field length of * 0 is invalid here. It probably is, but be lenient here * to avoid inconveniencing the user. */ goto bailout; } cur_ptr = hdr->attribute; vcr_len = *cur_ptr; cur_ptr++; sbuf_printf(sb, "\n\tVolume Change Reference Value:"); switch (vcr_len) { case 0: if (error_str != NULL) { snprintf(error_str, error_str_len, "Volume Change " "Reference value has length of 0"); } retval = 1; goto bailout; break; /*NOTREACHED*/ case 1: tmp_val = *cur_ptr; break; case 2: tmp_val = scsi_2btoul(cur_ptr); break; case 3: tmp_val = scsi_3btoul(cur_ptr); break; case 4: tmp_val = scsi_4btoul(cur_ptr); break; case 8: tmp_val = scsi_8btou64(cur_ptr); break; default: sbuf_printf(sb, "\n"); sbuf_hexdump(sb, cur_ptr, vcr_len, NULL, 0); break; } if (vcr_len <= 8) sbuf_printf(sb, " 0x%jx\n", (uintmax_t)tmp_val); cur_ptr += vcr_len; tmp_val = scsi_8btou64(cur_ptr); sbuf_printf(sb, "\tVolume Coherency Count: %ju\n", (uintmax_t)tmp_val); cur_ptr += sizeof(tmp_val); tmp_val = scsi_8btou64(cur_ptr); sbuf_printf(sb, "\tVolume Coherency Set Identifier: 0x%jx\n", (uintmax_t)tmp_val); /* * Figure out how long the Application Client Specific Information * is and produce a hexdump. */ cur_ptr += sizeof(tmp_val); as_len = scsi_2btoul(cur_ptr); cur_ptr += sizeof(uint16_t); sbuf_printf(sb, "\tApplication Client Specific Information: "); if (((as_len == SCSI_LTFS_VER0_LEN) || (as_len == SCSI_LTFS_VER1_LEN)) && (strncmp(cur_ptr, SCSI_LTFS_STR_NAME, SCSI_LTFS_STR_LEN) == 0)) { sbuf_printf(sb, "LTFS\n"); cur_ptr += SCSI_LTFS_STR_LEN + 1; if (cur_ptr[SCSI_LTFS_UUID_LEN] != '\0') cur_ptr[SCSI_LTFS_UUID_LEN] = '\0'; sbuf_printf(sb, "\tLTFS UUID: %s\n", cur_ptr); cur_ptr += SCSI_LTFS_UUID_LEN + 1; /* XXX KDM check the length */ sbuf_printf(sb, "\tLTFS Version: %d\n", *cur_ptr); } else { sbuf_printf(sb, "Unknown\n"); sbuf_hexdump(sb, cur_ptr, as_len, NULL, 0); } bailout: return (retval); } int scsi_attrib_vendser_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, uint32_t valid_len, uint32_t flags, uint32_t output_flags, char *error_str, int error_str_len) { size_t avail_len; uint32_t field_size; struct scsi_attrib_vendser *vendser; cam_strvis_flags strvis_flags; int retval = 0; field_size = scsi_2btoul(hdr->length); avail_len = valid_len - sizeof(*hdr); if (field_size > avail_len) { if (error_str != NULL) { snprintf(error_str, error_str_len, "Available " "length of attribute ID 0x%.4x %zu < field " "length %u", scsi_2btoul(hdr->id), avail_len, field_size); } retval = 1; goto bailout; } else if (field_size == 0) { /* * A field size of 0 doesn't make sense here. The device * can at least give you the vendor ID, even if it can't * give you the serial number. */ if (error_str != NULL) { snprintf(error_str, error_str_len, "The length of " "attribute ID 0x%.4x is 0", scsi_2btoul(hdr->id)); } retval = 1; goto bailout; } vendser = (struct scsi_attrib_vendser *)hdr->attribute; switch (output_flags & SCSI_ATTR_OUTPUT_NONASCII_MASK) { case SCSI_ATTR_OUTPUT_NONASCII_TRIM: strvis_flags = CAM_STRVIS_FLAG_NONASCII_TRIM; break; case SCSI_ATTR_OUTPUT_NONASCII_RAW: strvis_flags = CAM_STRVIS_FLAG_NONASCII_RAW; break; case SCSI_ATTR_OUTPUT_NONASCII_ESC: default: strvis_flags = CAM_STRVIS_FLAG_NONASCII_ESC; break;; } cam_strvis_sbuf(sb, vendser->vendor, sizeof(vendser->vendor), strvis_flags); sbuf_putc(sb, ' '); cam_strvis_sbuf(sb, vendser->serial_num, sizeof(vendser->serial_num), strvis_flags); bailout: return (retval); } int scsi_attrib_hexdump_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, uint32_t valid_len, uint32_t flags, uint32_t output_flags, char *error_str, int error_str_len) { uint32_t field_size; ssize_t avail_len; uint32_t print_len; uint8_t *num_ptr; int retval = 0; field_size = scsi_2btoul(hdr->length); avail_len = valid_len - sizeof(*hdr); print_len = MIN(avail_len, field_size); num_ptr = hdr->attribute; if (print_len > 0) { sbuf_printf(sb, "\n"); sbuf_hexdump(sb, num_ptr, print_len, NULL, 0); } return (retval); } int scsi_attrib_int_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, uint32_t valid_len, uint32_t flags, uint32_t output_flags, char *error_str, int error_str_len) { uint64_t print_number; size_t avail_len; uint32_t number_size; int retval = 0; number_size = scsi_2btoul(hdr->length); avail_len = valid_len - sizeof(*hdr); if (avail_len < number_size) { if (error_str != NULL) { snprintf(error_str, error_str_len, "Available " "length of attribute ID 0x%.4x %zu < field " "length %u", scsi_2btoul(hdr->id), avail_len, number_size); } retval = 1; goto bailout; } switch (number_size) { case 0: /* * We don't treat this as an error, since there may be * scenarios where a device reports a field but then gives * a length of 0. See the note in scsi_attrib_ascii_sbuf(). */ goto bailout; break; /*NOTREACHED*/ case 1: print_number = hdr->attribute[0]; break; case 2: print_number = scsi_2btoul(hdr->attribute); break; case 3: print_number = scsi_3btoul(hdr->attribute); break; case 4: print_number = scsi_4btoul(hdr->attribute); break; case 8: print_number = scsi_8btou64(hdr->attribute); break; default: /* * If we wind up here, the number is too big to print * normally, so just do a hexdump. */ retval = scsi_attrib_hexdump_sbuf(sb, hdr, valid_len, flags, output_flags, error_str, error_str_len); goto bailout; break; } if (flags & SCSI_ATTR_FLAG_FP) { #ifndef _KERNEL long double num_float; num_float = (long double)print_number; if (flags & SCSI_ATTR_FLAG_DIV_10) num_float /= 10; sbuf_printf(sb, "%.*Lf", (flags & SCSI_ATTR_FLAG_FP_1DIGIT) ? 1 : 0, num_float); #else /* _KERNEL */ sbuf_printf(sb, "%ju", (flags & SCSI_ATTR_FLAG_DIV_10) ? (print_number / 10) : print_number); #endif /* _KERNEL */ } else if (flags & SCSI_ATTR_FLAG_HEX) { sbuf_printf(sb, "0x%jx", (uintmax_t)print_number); } else sbuf_printf(sb, "%ju", (uintmax_t)print_number); bailout: return (retval); } int scsi_attrib_ascii_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, uint32_t valid_len, uint32_t flags, uint32_t output_flags, char *error_str, int error_str_len) { size_t avail_len; uint32_t field_size, print_size; int retval = 0; avail_len = valid_len - sizeof(*hdr); field_size = scsi_2btoul(hdr->length); print_size = MIN(avail_len, field_size); if (print_size > 0) { cam_strvis_flags strvis_flags; switch (output_flags & SCSI_ATTR_OUTPUT_NONASCII_MASK) { case SCSI_ATTR_OUTPUT_NONASCII_TRIM: strvis_flags = CAM_STRVIS_FLAG_NONASCII_TRIM; break; case SCSI_ATTR_OUTPUT_NONASCII_RAW: strvis_flags = CAM_STRVIS_FLAG_NONASCII_RAW; break; case SCSI_ATTR_OUTPUT_NONASCII_ESC: default: strvis_flags = CAM_STRVIS_FLAG_NONASCII_ESC; break; } cam_strvis_sbuf(sb, hdr->attribute, print_size, strvis_flags); } else if (avail_len < field_size) { /* * We only report an error if the user didn't allocate * enough space to hold the full value of this field. If * the field length is 0, that is allowed by the spec. * e.g. in SPC-4r37, section 7.4.2.2.5, VOLUME IDENTIFIER * "This attribute indicates the current volume identifier * (see SMC-3) of the medium. If the device server supports * this attribute but does not have access to the volume * identifier, the device server shall report this attribute * with an attribute length value of zero." */ if (error_str != NULL) { snprintf(error_str, error_str_len, "Available " "length of attribute ID 0x%.4x %zu < field " "length %u", scsi_2btoul(hdr->id), avail_len, field_size); } retval = 1; } return (retval); } int scsi_attrib_text_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, uint32_t valid_len, uint32_t flags, uint32_t output_flags, char *error_str, int error_str_len) { size_t avail_len; uint32_t field_size, print_size; int retval = 0; int esc_text = 1; avail_len = valid_len - sizeof(*hdr); field_size = scsi_2btoul(hdr->length); print_size = MIN(avail_len, field_size); if ((output_flags & SCSI_ATTR_OUTPUT_TEXT_MASK) == SCSI_ATTR_OUTPUT_TEXT_RAW) esc_text = 0; if (print_size > 0) { uint32_t i; for (i = 0; i < print_size; i++) { if (hdr->attribute[i] == '\0') continue; else if (((unsigned char)hdr->attribute[i] < 0x80) || (esc_text == 0)) sbuf_putc(sb, hdr->attribute[i]); else sbuf_printf(sb, "%%%02x", (unsigned char)hdr->attribute[i]); } } else if (avail_len < field_size) { /* * We only report an error if the user didn't allocate * enough space to hold the full value of this field. */ if (error_str != NULL) { snprintf(error_str, error_str_len, "Available " "length of attribute ID 0x%.4x %zu < field " "length %u", scsi_2btoul(hdr->id), avail_len, field_size); } retval = 1; } return (retval); } struct scsi_attrib_table_entry * scsi_find_attrib_entry(struct scsi_attrib_table_entry *table, size_t num_table_entries, uint32_t id) { uint32_t i; for (i = 0; i < num_table_entries; i++) { if (table[i].id == id) return (&table[i]); } return (NULL); } struct scsi_attrib_table_entry * scsi_get_attrib_entry(uint32_t id) { return (scsi_find_attrib_entry(scsi_mam_attr_table, nitems(scsi_mam_attr_table), id)); } int scsi_attrib_value_sbuf(struct sbuf *sb, uint32_t valid_len, struct scsi_mam_attribute_header *hdr, uint32_t output_flags, char *error_str, size_t error_str_len) { int retval; switch (hdr->byte2 & SMA_FORMAT_MASK) { case SMA_FORMAT_ASCII: retval = scsi_attrib_ascii_sbuf(sb, hdr, valid_len, SCSI_ATTR_FLAG_NONE, output_flags, error_str,error_str_len); break; case SMA_FORMAT_BINARY: if (scsi_2btoul(hdr->length) <= 8) retval = scsi_attrib_int_sbuf(sb, hdr, valid_len, SCSI_ATTR_FLAG_NONE, output_flags, error_str, error_str_len); else retval = scsi_attrib_hexdump_sbuf(sb, hdr, valid_len, SCSI_ATTR_FLAG_NONE, output_flags, error_str, error_str_len); break; case SMA_FORMAT_TEXT: retval = scsi_attrib_text_sbuf(sb, hdr, valid_len, SCSI_ATTR_FLAG_NONE, output_flags, error_str, error_str_len); break; default: if (error_str != NULL) { snprintf(error_str, error_str_len, "Unknown attribute " "format 0x%x", hdr->byte2 & SMA_FORMAT_MASK); } retval = 1; goto bailout; break; /*NOTREACHED*/ } sbuf_trim(sb); bailout: return (retval); } void scsi_attrib_prefix_sbuf(struct sbuf *sb, uint32_t output_flags, struct scsi_mam_attribute_header *hdr, uint32_t valid_len, const char *desc) { int need_space = 0; uint32_t len; uint32_t id; /* * We can't do anything if we don't have enough valid data for the * header. */ if (valid_len < sizeof(*hdr)) return; id = scsi_2btoul(hdr->id); /* * Note that we print out the value of the attribute listed in the * header, regardless of whether we actually got that many bytes * back from the device through the controller. A truncated result * could be the result of a failure to ask for enough data; the * header indicates how many bytes are allocated for this attribute * in the MAM. */ len = scsi_2btoul(hdr->length); if ((output_flags & SCSI_ATTR_OUTPUT_FIELD_MASK) == SCSI_ATTR_OUTPUT_FIELD_NONE) return; if ((output_flags & SCSI_ATTR_OUTPUT_FIELD_DESC) && (desc != NULL)) { sbuf_printf(sb, "%s", desc); need_space = 1; } if (output_flags & SCSI_ATTR_OUTPUT_FIELD_NUM) { sbuf_printf(sb, "%s(0x%.4x)", (need_space) ? " " : "", id); need_space = 0; } if (output_flags & SCSI_ATTR_OUTPUT_FIELD_SIZE) { sbuf_printf(sb, "%s[%d]", (need_space) ? " " : "", len); need_space = 0; } if (output_flags & SCSI_ATTR_OUTPUT_FIELD_RW) { sbuf_printf(sb, "%s(%s)", (need_space) ? " " : "", (hdr->byte2 & SMA_READ_ONLY) ? "RO" : "RW"); } sbuf_printf(sb, ": "); } int scsi_attrib_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, uint32_t valid_len, struct scsi_attrib_table_entry *user_table, size_t num_user_entries, int prefer_user_table, uint32_t output_flags, char *error_str, int error_str_len) { int retval; struct scsi_attrib_table_entry *table1 = NULL, *table2 = NULL; struct scsi_attrib_table_entry *entry = NULL; size_t table1_size = 0, table2_size = 0; uint32_t id; retval = 0; if (valid_len < sizeof(*hdr)) { retval = 1; goto bailout; } id = scsi_2btoul(hdr->id); if (user_table != NULL) { if (prefer_user_table != 0) { table1 = user_table; table1_size = num_user_entries; table2 = scsi_mam_attr_table; table2_size = nitems(scsi_mam_attr_table); } else { table1 = scsi_mam_attr_table; table1_size = nitems(scsi_mam_attr_table); table2 = user_table; table2_size = num_user_entries; } } else { table1 = scsi_mam_attr_table; table1_size = nitems(scsi_mam_attr_table); } entry = scsi_find_attrib_entry(table1, table1_size, id); if (entry != NULL) { scsi_attrib_prefix_sbuf(sb, output_flags, hdr, valid_len, entry->desc); if (entry->to_str == NULL) goto print_default; retval = entry->to_str(sb, hdr, valid_len, entry->flags, output_flags, error_str, error_str_len); goto bailout; } if (table2 != NULL) { entry = scsi_find_attrib_entry(table2, table2_size, id); if (entry != NULL) { if (entry->to_str == NULL) goto print_default; scsi_attrib_prefix_sbuf(sb, output_flags, hdr, valid_len, entry->desc); retval = entry->to_str(sb, hdr, valid_len, entry->flags, output_flags, error_str, error_str_len); goto bailout; } } scsi_attrib_prefix_sbuf(sb, output_flags, hdr, valid_len, NULL); print_default: retval = scsi_attrib_value_sbuf(sb, valid_len, hdr, output_flags, error_str, error_str_len); bailout: if (retval == 0) { if ((entry != NULL) && (entry->suffix != NULL)) sbuf_printf(sb, " %s", entry->suffix); sbuf_trim(sb); sbuf_printf(sb, "\n"); } return (retval); } void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_test_unit_ready *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_test_unit_ready *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = TEST_UNIT_READY; } void scsi_request_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), void *data_ptr, u_int8_t dxfer_len, u_int8_t tag_action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_request_sense *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_request_sense *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REQUEST_SENSE; scsi_cmd->length = dxfer_len; } void scsi_inquiry(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t *inq_buf, u_int32_t inq_len, int evpd, u_int8_t page_code, u_int8_t sense_len, u_int32_t timeout) { struct scsi_inquiry *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/inq_buf, /*dxfer_len*/inq_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_inquiry *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = INQUIRY; if (evpd) { scsi_cmd->byte2 |= SI_EVPD; scsi_cmd->page_code = page_code; } scsi_ulto2b(inq_len, scsi_cmd->length); } void scsi_mode_sense(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, int dbd, uint8_t pc, uint8_t page, uint8_t *param_buf, uint32_t param_len, uint8_t sense_len, uint32_t timeout) { scsi_mode_sense_subpage(csio, retries, cbfcnp, tag_action, dbd, pc, page, 0, param_buf, param_len, 0, sense_len, timeout); } void scsi_mode_sense_len(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, int dbd, uint8_t pc, uint8_t page, uint8_t *param_buf, uint32_t param_len, int minimum_cmd_size, uint8_t sense_len, uint32_t timeout) { scsi_mode_sense_subpage(csio, retries, cbfcnp, tag_action, dbd, pc, page, 0, param_buf, param_len, minimum_cmd_size, sense_len, timeout); } void scsi_mode_sense_subpage(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, int dbd, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t *param_buf, uint32_t param_len, int minimum_cmd_size, uint8_t sense_len, uint32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ if ((param_len < 256) && (minimum_cmd_size < 10)) { /* * We can fit in a 6 byte cdb. */ struct scsi_mode_sense_6 *scsi_cmd; scsi_cmd = (struct scsi_mode_sense_6 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SENSE_6; if (dbd != 0) scsi_cmd->byte2 |= SMS_DBD; scsi_cmd->page = pc | page; scsi_cmd->subpage = subpage; scsi_cmd->length = param_len; cdb_len = sizeof(*scsi_cmd); } else { /* * Need a 10 byte cdb. */ struct scsi_mode_sense_10 *scsi_cmd; scsi_cmd = (struct scsi_mode_sense_10 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SENSE_10; if (dbd != 0) scsi_cmd->byte2 |= SMS_DBD; scsi_cmd->page = pc | page; scsi_cmd->subpage = subpage; scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action, param_buf, param_len, sense_len, cdb_len, timeout); } void scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int scsi_page_fmt, int save_pages, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { scsi_mode_select_len(csio, retries, cbfcnp, tag_action, scsi_page_fmt, save_pages, param_buf, param_len, 0, sense_len, timeout); } void scsi_mode_select_len(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int scsi_page_fmt, int save_pages, u_int8_t *param_buf, u_int32_t param_len, int minimum_cmd_size, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ if ((param_len < 256) && (minimum_cmd_size < 10)) { /* * We can fit in a 6 byte cdb. */ struct scsi_mode_select_6 *scsi_cmd; scsi_cmd = (struct scsi_mode_select_6 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SELECT_6; if (scsi_page_fmt != 0) scsi_cmd->byte2 |= SMS_PF; if (save_pages != 0) scsi_cmd->byte2 |= SMS_SP; scsi_cmd->length = param_len; cdb_len = sizeof(*scsi_cmd); } else { /* * Need a 10 byte cdb. */ struct scsi_mode_select_10 *scsi_cmd; scsi_cmd = (struct scsi_mode_select_10 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SELECT_10; if (scsi_page_fmt != 0) scsi_cmd->byte2 |= SMS_PF; if (save_pages != 0) scsi_cmd->byte2 |= SMS_SP; scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_OUT, tag_action, param_buf, param_len, sense_len, cdb_len, timeout); } void scsi_log_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t page_code, u_int8_t page, int save_pages, int ppc, u_int32_t paramptr, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_log_sense *scsi_cmd; u_int8_t cdb_len; scsi_cmd = (struct scsi_log_sense *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = LOG_SENSE; scsi_cmd->page = page_code | page; if (save_pages != 0) scsi_cmd->byte2 |= SLS_SP; if (ppc != 0) scsi_cmd->byte2 |= SLS_PPC; scsi_ulto2b(paramptr, scsi_cmd->paramptr); scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/param_buf, /*dxfer_len*/param_len, sense_len, cdb_len, timeout); } void scsi_log_select(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t page_code, int save_pages, int pc_reset, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_log_select *scsi_cmd; u_int8_t cdb_len; scsi_cmd = (struct scsi_log_select *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = LOG_SELECT; scsi_cmd->page = page_code & SLS_PAGE_CODE; if (save_pages != 0) scsi_cmd->byte2 |= SLS_SP; if (pc_reset != 0) scsi_cmd->byte2 |= SLS_PCR; scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, /*data_ptr*/param_buf, /*dxfer_len*/param_len, sense_len, cdb_len, timeout); } /* * Prevent or allow the user to remove the media */ void scsi_prevent(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_prevent *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_prevent *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = PREVENT_ALLOW; scsi_cmd->how = action; } /* XXX allow specification of address and PMI bit and LBA */ void scsi_read_capacity(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, struct scsi_read_capacity_data *rcap_buf, u_int8_t sense_len, u_int32_t timeout) { struct scsi_read_capacity *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)rcap_buf, /*dxfer_len*/sizeof(*rcap_buf), sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_read_capacity *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = READ_CAPACITY; } void scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, uint64_t lba, int reladr, int pmi, uint8_t *rcap_buf, int rcap_buf_len, uint8_t sense_len, uint32_t timeout) { struct scsi_read_capacity_16 *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)rcap_buf, /*dxfer_len*/rcap_buf_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_read_capacity_16 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SERVICE_ACTION_IN; scsi_cmd->service_action = SRC16_SERVICE_ACTION; scsi_u64to8b(lba, scsi_cmd->addr); scsi_ulto4b(rcap_buf_len, scsi_cmd->alloc_len); if (pmi) reladr |= SRC16_PMI; if (reladr) reladr |= SRC16_RELADR; } void scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t select_report, struct scsi_report_luns_data *rpl_buf, u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_report_luns *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)rpl_buf, /*dxfer_len*/alloc_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_report_luns *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REPORT_LUNS; scsi_cmd->select_report = select_report; scsi_ulto4b(alloc_len, scsi_cmd->length); } void scsi_report_target_group(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t pdf, void *buf, u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_target_group *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)buf, /*dxfer_len*/alloc_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_target_group *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MAINTENANCE_IN; scsi_cmd->service_action = REPORT_TARGET_PORT_GROUPS | pdf; scsi_ulto4b(alloc_len, scsi_cmd->length); } void scsi_report_timestamp(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t pdf, void *buf, u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_timestamp *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)buf, /*dxfer_len*/alloc_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_timestamp *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MAINTENANCE_IN; scsi_cmd->service_action = REPORT_TIMESTAMP | pdf; scsi_ulto4b(alloc_len, scsi_cmd->length); } void scsi_set_target_group(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, void *buf, u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_target_group *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, /*data_ptr*/(u_int8_t *)buf, /*dxfer_len*/alloc_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_target_group *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MAINTENANCE_OUT; scsi_cmd->service_action = SET_TARGET_PORT_GROUPS; scsi_ulto4b(alloc_len, scsi_cmd->length); } void scsi_create_timestamp(uint8_t *timestamp_6b_buf, uint64_t timestamp) { uint8_t buf[8]; scsi_u64to8b(timestamp, buf); /* * Using memcopy starting at buf[2] because the set timestamp parameters * only has six bytes for the timestamp to fit into, and we don't have a * scsi_u64to6b function. */ memcpy(timestamp_6b_buf, &buf[2], 6); } void scsi_set_timestamp(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, void *buf, u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_timestamp *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, /*data_ptr*/(u_int8_t *) buf, /*dxfer_len*/alloc_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_timestamp *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MAINTENANCE_OUT; scsi_cmd->service_action = SET_TIMESTAMP; scsi_ulto4b(alloc_len, scsi_cmd->length); } /* * Syncronize the media to the contents of the cache for * the given lba/count pair. Specifying 0/0 means sync * the whole cache. */ void scsi_synchronize_cache(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int32_t begin_lba, u_int16_t lb_count, u_int8_t sense_len, u_int32_t timeout) { struct scsi_sync_cache *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_sync_cache *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SYNCHRONIZE_CACHE; scsi_ulto4b(begin_lba, scsi_cmd->begin_lba); scsi_ulto2b(lb_count, scsi_cmd->lb_count); } void scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int readop, u_int8_t byte2, int minimum_cmd_size, u_int64_t lba, u_int32_t block_count, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { int read; u_int8_t cdb_len; read = (readop & SCSI_RW_DIRMASK) == SCSI_RW_READ; /* * Use the smallest possible command to perform the operation * as some legacy hardware does not support the 10 byte commands. * If any of the bits in byte2 is set, we have to go with a larger * command. */ if ((minimum_cmd_size < 10) && ((lba & 0x1fffff) == lba) && ((block_count & 0xff) == block_count) && (byte2 == 0)) { /* * We can fit in a 6 byte cdb. */ struct scsi_rw_6 *scsi_cmd; scsi_cmd = (struct scsi_rw_6 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = read ? READ_6 : WRITE_6; scsi_ulto3b(lba, scsi_cmd->addr); scsi_cmd->length = block_count & 0xff; scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("6byte: %x%x%x:%d:%d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->length, dxfer_len)); } else if ((minimum_cmd_size < 12) && ((block_count & 0xffff) == block_count) && ((lba & 0xffffffff) == lba)) { /* * Need a 10 byte cdb. */ struct scsi_rw_10 *scsi_cmd; scsi_cmd = (struct scsi_rw_10 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = read ? READ_10 : WRITE_10; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; scsi_ulto2b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("10byte: %x%x%x%x:%x%x: %d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->addr[3], scsi_cmd->length[0], scsi_cmd->length[1], dxfer_len)); } else if ((minimum_cmd_size < 16) && ((block_count & 0xffffffff) == block_count) && ((lba & 0xffffffff) == lba)) { /* * The block count is too big for a 10 byte CDB, use a 12 * byte CDB. */ struct scsi_rw_12 *scsi_cmd; scsi_cmd = (struct scsi_rw_12 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = read ? READ_12 : WRITE_12; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; scsi_ulto4b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("12byte: %x%x%x%x:%x%x%x%x: %d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->addr[3], scsi_cmd->length[0], scsi_cmd->length[1], scsi_cmd->length[2], scsi_cmd->length[3], dxfer_len)); } else { /* * 16 byte CDB. We'll only get here if the LBA is larger * than 2^32, or if the user asks for a 16 byte command. */ struct scsi_rw_16 *scsi_cmd; scsi_cmd = (struct scsi_rw_16 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = read ? READ_16 : WRITE_16; scsi_cmd->byte2 = byte2; scsi_u64to8b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; scsi_ulto4b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, retries, cbfcnp, (read ? CAM_DIR_IN : CAM_DIR_OUT) | ((readop & SCSI_RW_BIO) != 0 ? CAM_DATA_BIO : 0), tag_action, data_ptr, dxfer_len, sense_len, cdb_len, timeout); } void scsi_write_same(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t byte2, int minimum_cmd_size, u_int64_t lba, u_int32_t block_count, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; if ((minimum_cmd_size < 16) && ((block_count & 0xffff) == block_count) && ((lba & 0xffffffff) == lba)) { /* * Need a 10 byte cdb. */ struct scsi_write_same_10 *scsi_cmd; scsi_cmd = (struct scsi_write_same_10 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = WRITE_SAME_10; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->group = 0; scsi_ulto2b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("10byte: %x%x%x%x:%x%x: %d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->addr[3], scsi_cmd->length[0], scsi_cmd->length[1], dxfer_len)); } else { /* * 16 byte CDB. We'll only get here if the LBA is larger * than 2^32, or if the user asks for a 16 byte command. */ struct scsi_write_same_16 *scsi_cmd; scsi_cmd = (struct scsi_write_same_16 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = WRITE_SAME_16; scsi_cmd->byte2 = byte2; scsi_u64to8b(lba, scsi_cmd->addr); scsi_ulto4b(block_count, scsi_cmd->length); scsi_cmd->group = 0; scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("16byte: %x%x%x%x%x%x%x%x:%x%x%x%x: %d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->addr[3], scsi_cmd->addr[4], scsi_cmd->addr[5], scsi_cmd->addr[6], scsi_cmd->addr[7], scsi_cmd->length[0], scsi_cmd->length[1], scsi_cmd->length[2], scsi_cmd->length[3], dxfer_len)); } cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, data_ptr, dxfer_len, sense_len, cdb_len, timeout); } void scsi_ata_identify(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t *data_ptr, u_int16_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { scsi_ata_pass(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*protocol*/AP_PROTO_PIO_IN, /*ata_flags*/AP_FLAG_TDIR_FROM_DEV | AP_FLAG_BYT_BLOK_BYTES | AP_FLAG_TLEN_SECT_CNT, /*features*/0, /*sector_count*/dxfer_len, /*lba*/0, /*command*/ATA_ATA_IDENTIFY, /*device*/ 0, /*icc*/ 0, /*auxiliary*/ 0, /*control*/0, data_ptr, dxfer_len, /*cdb_storage*/ NULL, /*cdb_storage_len*/ 0, /*minimum_cmd_size*/ 0, sense_len, timeout); } void scsi_ata_trim(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int16_t block_count, u_int8_t *data_ptr, u_int16_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { scsi_ata_pass_16(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, /*protocol*/AP_EXTEND|AP_PROTO_DMA, /*ata_flags*/AP_FLAG_TLEN_SECT_CNT|AP_FLAG_BYT_BLOK_BLOCKS, /*features*/ATA_DSM_TRIM, /*sector_count*/block_count, /*lba*/0, /*command*/ATA_DATA_SET_MANAGEMENT, /*control*/0, data_ptr, dxfer_len, sense_len, timeout); } int scsi_ata_read_log(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, uint32_t log_address, uint32_t page_number, uint16_t block_count, uint8_t protocol, uint8_t *data_ptr, uint32_t dxfer_len, uint8_t sense_len, uint32_t timeout) { uint8_t command, protocol_out; uint16_t count_out; uint64_t lba; int retval; retval = 0; switch (protocol) { case AP_PROTO_DMA: count_out = block_count; command = ATA_READ_LOG_DMA_EXT; protocol_out = AP_PROTO_DMA; break; case AP_PROTO_PIO_IN: default: count_out = block_count; command = ATA_READ_LOG_EXT; protocol_out = AP_PROTO_PIO_IN; break; } lba = (((uint64_t)page_number & 0xff00) << 32) | ((page_number & 0x00ff) << 8) | (log_address & 0xff); protocol_out |= AP_EXTEND; retval = scsi_ata_pass(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*protocol*/ protocol_out, /*ata_flags*/AP_FLAG_TLEN_SECT_CNT | AP_FLAG_BYT_BLOK_BLOCKS | AP_FLAG_TDIR_FROM_DEV, /*feature*/ 0, /*sector_count*/ count_out, /*lba*/ lba, /*command*/ command, /*device*/ 0, /*icc*/ 0, /*auxiliary*/ 0, /*control*/0, data_ptr, dxfer_len, /*cdb_storage*/ NULL, /*cdb_storage_len*/ 0, /*minimum_cmd_size*/ 0, sense_len, timeout); return (retval); } int scsi_ata_setfeatures(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, uint8_t feature, uint64_t lba, uint32_t count, uint8_t sense_len, uint32_t timeout) { return (scsi_ata_pass(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE, tag_action, /*protocol*/AP_PROTO_PIO_IN, /*ata_flags*/AP_FLAG_TDIR_FROM_DEV | AP_FLAG_BYT_BLOK_BYTES | AP_FLAG_TLEN_SECT_CNT, /*features*/feature, /*sector_count*/count, /*lba*/lba, /*command*/ATA_SETFEATURES, /*device*/ 0, /*icc*/ 0, /*auxiliary*/0, /*control*/0, /*data_ptr*/NULL, /*dxfer_len*/0, /*cdb_storage*/NULL, /*cdb_storage_len*/0, /*minimum_cmd_size*/0, sense_len, timeout)); } /* * Note! This is an unusual CDB building function because it can return * an error in the event that the command in question requires a variable * length CDB, but the caller has not given storage space for one or has not * given enough storage space. If there is enough space available in the * standard SCSI CCB CDB bytes, we'll prefer that over passed in storage. */ int scsi_ata_pass(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags, uint8_t tag_action, uint8_t protocol, uint8_t ata_flags, uint16_t features, uint16_t sector_count, uint64_t lba, uint8_t command, uint8_t device, uint8_t icc, uint32_t auxiliary, uint8_t control, u_int8_t *data_ptr, uint32_t dxfer_len, uint8_t *cdb_storage, size_t cdb_storage_len, int minimum_cmd_size, u_int8_t sense_len, u_int32_t timeout) { uint32_t cam_flags; uint8_t *cdb_ptr; int cmd_size; int retval; uint8_t cdb_len; retval = 0; cam_flags = flags; /* * Round the user's request to the nearest command size that is at * least as big as what he requested. */ if (minimum_cmd_size <= 12) cmd_size = 12; else if (minimum_cmd_size > 16) cmd_size = 32; else cmd_size = 16; /* * If we have parameters that require a 48-bit ATA command, we have to * use the 16 byte ATA PASS-THROUGH command at least. */ if (((lba > ATA_MAX_28BIT_LBA) || (sector_count > 255) || (features > 255) || (protocol & AP_EXTEND)) && ((cmd_size < 16) || ((protocol & AP_EXTEND) == 0))) { if (cmd_size < 16) cmd_size = 16; protocol |= AP_EXTEND; } /* * The icc and auxiliary ATA registers are only supported in the * 32-byte version of the ATA PASS-THROUGH command. */ if ((icc != 0) || (auxiliary != 0)) { cmd_size = 32; protocol |= AP_EXTEND; } if ((cmd_size > sizeof(csio->cdb_io.cdb_bytes)) && ((cdb_storage == NULL) || (cdb_storage_len < cmd_size))) { retval = 1; goto bailout; } /* * At this point we know we have enough space to store the command * in one place or another. We prefer the built-in array, but used * the passed in storage if necessary. */ if (cmd_size <= sizeof(csio->cdb_io.cdb_bytes)) cdb_ptr = csio->cdb_io.cdb_bytes; else { cdb_ptr = cdb_storage; cam_flags |= CAM_CDB_POINTER; } if (cmd_size <= 12) { struct ata_pass_12 *cdb; cdb = (struct ata_pass_12 *)cdb_ptr; cdb_len = sizeof(*cdb); bzero(cdb, cdb_len); cdb->opcode = ATA_PASS_12; cdb->protocol = protocol; cdb->flags = ata_flags; cdb->features = features; cdb->sector_count = sector_count; cdb->lba_low = lba & 0xff; cdb->lba_mid = (lba >> 8) & 0xff; cdb->lba_high = (lba >> 16) & 0xff; cdb->device = ((lba >> 24) & 0xf) | ATA_DEV_LBA; cdb->command = command; cdb->control = control; } else if (cmd_size <= 16) { struct ata_pass_16 *cdb; cdb = (struct ata_pass_16 *)cdb_ptr; cdb_len = sizeof(*cdb); bzero(cdb, cdb_len); cdb->opcode = ATA_PASS_16; cdb->protocol = protocol; cdb->flags = ata_flags; cdb->features = features & 0xff; cdb->sector_count = sector_count & 0xff; cdb->lba_low = lba & 0xff; cdb->lba_mid = (lba >> 8) & 0xff; cdb->lba_high = (lba >> 16) & 0xff; /* * If AP_EXTEND is set, we're sending a 48-bit command. * Otherwise it's a 28-bit command. */ if (protocol & AP_EXTEND) { cdb->lba_low_ext = (lba >> 24) & 0xff; cdb->lba_mid_ext = (lba >> 32) & 0xff; cdb->lba_high_ext = (lba >> 40) & 0xff; cdb->features_ext = (features >> 8) & 0xff; cdb->sector_count_ext = (sector_count >> 8) & 0xff; cdb->device = device | ATA_DEV_LBA; } else { cdb->lba_low_ext = (lba >> 24) & 0xf; cdb->device = ((lba >> 24) & 0xf) | ATA_DEV_LBA; } cdb->command = command; cdb->control = control; } else { struct ata_pass_32 *cdb; uint8_t tmp_lba[8]; cdb = (struct ata_pass_32 *)cdb_ptr; cdb_len = sizeof(*cdb); bzero(cdb, cdb_len); cdb->opcode = VARIABLE_LEN_CDB; cdb->control = control; cdb->length = sizeof(*cdb) - __offsetof(struct ata_pass_32, service_action); scsi_ulto2b(ATA_PASS_32_SA, cdb->service_action); cdb->protocol = protocol; cdb->flags = ata_flags; if ((protocol & AP_EXTEND) == 0) { lba &= 0x0fffffff; cdb->device = ((lba >> 24) & 0xf) | ATA_DEV_LBA; features &= 0xff; sector_count &= 0xff; } else { cdb->device = device | ATA_DEV_LBA; } scsi_u64to8b(lba, tmp_lba); bcopy(&tmp_lba[2], cdb->lba, sizeof(cdb->lba)); scsi_ulto2b(features, cdb->features); scsi_ulto2b(sector_count, cdb->count); cdb->command = command; cdb->icc = icc; scsi_ulto4b(auxiliary, cdb->auxiliary); } cam_fill_csio(csio, retries, cbfcnp, cam_flags, tag_action, data_ptr, dxfer_len, sense_len, cmd_size, timeout); bailout: return (retval); } void scsi_ata_pass_16(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int32_t flags, u_int8_t tag_action, u_int8_t protocol, u_int8_t ata_flags, u_int16_t features, u_int16_t sector_count, uint64_t lba, u_int8_t command, u_int8_t control, u_int8_t *data_ptr, u_int16_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct ata_pass_16 *ata_cmd; ata_cmd = (struct ata_pass_16 *)&csio->cdb_io.cdb_bytes; ata_cmd->opcode = ATA_PASS_16; ata_cmd->protocol = protocol; ata_cmd->flags = ata_flags; ata_cmd->features_ext = features >> 8; ata_cmd->features = features; ata_cmd->sector_count_ext = sector_count >> 8; ata_cmd->sector_count = sector_count; ata_cmd->lba_low = lba; ata_cmd->lba_mid = lba >> 8; ata_cmd->lba_high = lba >> 16; ata_cmd->device = ATA_DEV_LBA; if (protocol & AP_EXTEND) { ata_cmd->lba_low_ext = lba >> 24; ata_cmd->lba_mid_ext = lba >> 32; ata_cmd->lba_high_ext = lba >> 40; } else ata_cmd->device |= (lba >> 24) & 0x0f; ata_cmd->command = command; ata_cmd->control = control; cam_fill_csio(csio, retries, cbfcnp, flags, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*ata_cmd), timeout); } void scsi_unmap(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t byte2, u_int8_t *data_ptr, u_int16_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_unmap *scsi_cmd; scsi_cmd = (struct scsi_unmap *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = UNMAP; scsi_cmd->byte2 = byte2; scsi_ulto4b(0, scsi_cmd->reserved); scsi_cmd->group = 0; scsi_ulto2b(dxfer_len, scsi_cmd->length); scsi_cmd->control = 0; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_receive_diagnostic_results(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb*), uint8_t tag_action, int pcv, uint8_t page_code, uint8_t *data_ptr, uint16_t allocation_length, uint8_t sense_len, uint32_t timeout) { struct scsi_receive_diag *scsi_cmd; scsi_cmd = (struct scsi_receive_diag *)&csio->cdb_io.cdb_bytes; memset(scsi_cmd, 0, sizeof(*scsi_cmd)); scsi_cmd->opcode = RECEIVE_DIAGNOSTIC; if (pcv) { scsi_cmd->byte2 |= SRD_PCV; scsi_cmd->page_code = page_code; } scsi_ulto2b(allocation_length, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, data_ptr, allocation_length, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_send_diagnostic(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, int unit_offline, int device_offline, int self_test, int page_format, int self_test_code, uint8_t *data_ptr, uint16_t param_list_length, uint8_t sense_len, uint32_t timeout) { struct scsi_send_diag *scsi_cmd; scsi_cmd = (struct scsi_send_diag *)&csio->cdb_io.cdb_bytes; memset(scsi_cmd, 0, sizeof(*scsi_cmd)); scsi_cmd->opcode = SEND_DIAGNOSTIC; /* * The default self-test mode control and specific test * control are mutually exclusive. */ if (self_test) self_test_code = SSD_SELF_TEST_CODE_NONE; scsi_cmd->byte2 = ((self_test_code << SSD_SELF_TEST_CODE_SHIFT) & SSD_SELF_TEST_CODE_MASK) | (unit_offline ? SSD_UNITOFFL : 0) | (device_offline ? SSD_DEVOFFL : 0) | (self_test ? SSD_SELFTEST : 0) | (page_format ? SSD_PF : 0); scsi_ulto2b(param_list_length, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/param_list_length ? CAM_DIR_OUT : CAM_DIR_NONE, tag_action, data_ptr, param_list_length, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_read_buffer(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb*), uint8_t tag_action, int mode, uint8_t buffer_id, u_int32_t offset, uint8_t *data_ptr, uint32_t allocation_length, uint8_t sense_len, uint32_t timeout) { struct scsi_read_buffer *scsi_cmd; scsi_cmd = (struct scsi_read_buffer *)&csio->cdb_io.cdb_bytes; memset(scsi_cmd, 0, sizeof(*scsi_cmd)); scsi_cmd->opcode = READ_BUFFER; scsi_cmd->byte2 = mode; scsi_cmd->buffer_id = buffer_id; scsi_ulto3b(offset, scsi_cmd->offset); scsi_ulto3b(allocation_length, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, data_ptr, allocation_length, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_write_buffer(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, int mode, uint8_t buffer_id, u_int32_t offset, uint8_t *data_ptr, uint32_t param_list_length, uint8_t sense_len, uint32_t timeout) { struct scsi_write_buffer *scsi_cmd; scsi_cmd = (struct scsi_write_buffer *)&csio->cdb_io.cdb_bytes; memset(scsi_cmd, 0, sizeof(*scsi_cmd)); scsi_cmd->opcode = WRITE_BUFFER; scsi_cmd->byte2 = mode; scsi_cmd->buffer_id = buffer_id; scsi_ulto3b(offset, scsi_cmd->offset); scsi_ulto3b(param_list_length, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/param_list_length ? CAM_DIR_OUT : CAM_DIR_NONE, tag_action, data_ptr, param_list_length, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int start, int load_eject, int immediate, u_int8_t sense_len, u_int32_t timeout) { struct scsi_start_stop_unit *scsi_cmd; int extra_flags = 0; scsi_cmd = (struct scsi_start_stop_unit *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = START_STOP_UNIT; if (start != 0) { scsi_cmd->how |= SSS_START; /* it takes a lot of power to start a drive */ extra_flags |= CAM_HIGH_POWER; } if (load_eject != 0) scsi_cmd->how |= SSS_LOEJ; if (immediate != 0) scsi_cmd->byte2 |= SSS_IMMED; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE | extra_flags, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_read_attribute(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t service_action, uint32_t element, u_int8_t elem_type, int logical_volume, int partition, u_int32_t first_attribute, int cache, u_int8_t *data_ptr, u_int32_t length, int sense_len, u_int32_t timeout) { struct scsi_read_attribute *scsi_cmd; scsi_cmd = (struct scsi_read_attribute *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = READ_ATTRIBUTE; scsi_cmd->service_action = service_action; scsi_ulto2b(element, scsi_cmd->element); scsi_cmd->elem_type = elem_type; scsi_cmd->logical_volume = logical_volume; scsi_cmd->partition = partition; scsi_ulto2b(first_attribute, scsi_cmd->first_attribute); scsi_ulto4b(length, scsi_cmd->length); if (cache != 0) scsi_cmd->cache |= SRA_CACHE; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/data_ptr, /*dxfer_len*/length, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_write_attribute(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, uint32_t element, int logical_volume, int partition, int wtc, u_int8_t *data_ptr, u_int32_t length, int sense_len, u_int32_t timeout) { struct scsi_write_attribute *scsi_cmd; scsi_cmd = (struct scsi_write_attribute *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = WRITE_ATTRIBUTE; if (wtc != 0) scsi_cmd->byte2 = SWA_WTC; scsi_ulto3b(element, scsi_cmd->element); scsi_cmd->logical_volume = logical_volume; scsi_cmd->partition = partition; scsi_ulto4b(length, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, /*data_ptr*/data_ptr, /*dxfer_len*/length, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_persistent_reserve_in(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, int service_action, uint8_t *data_ptr, uint32_t dxfer_len, int sense_len, int timeout) { struct scsi_per_res_in *scsi_cmd; scsi_cmd = (struct scsi_per_res_in *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = PERSISTENT_RES_IN; scsi_cmd->action = service_action; scsi_ulto2b(dxfer_len, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_persistent_reserve_out(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, int service_action, int scope, int res_type, uint8_t *data_ptr, uint32_t dxfer_len, int sense_len, int timeout) { struct scsi_per_res_out *scsi_cmd; scsi_cmd = (struct scsi_per_res_out *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = PERSISTENT_RES_OUT; scsi_cmd->action = service_action; scsi_cmd->scope_type = scope | res_type; scsi_ulto4b(dxfer_len, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, /*data_ptr*/data_ptr, /*dxfer_len*/dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_security_protocol_in(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, uint32_t security_protocol, uint32_t security_protocol_specific, int byte4, uint8_t *data_ptr, uint32_t dxfer_len, int sense_len, int timeout) { struct scsi_security_protocol_in *scsi_cmd; scsi_cmd = (struct scsi_security_protocol_in *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SECURITY_PROTOCOL_IN; scsi_cmd->security_protocol = security_protocol; scsi_ulto2b(security_protocol_specific, scsi_cmd->security_protocol_specific); scsi_cmd->byte4 = byte4; scsi_ulto4b(dxfer_len, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_security_protocol_out(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, uint32_t security_protocol, uint32_t security_protocol_specific, int byte4, uint8_t *data_ptr, uint32_t dxfer_len, int sense_len, int timeout) { struct scsi_security_protocol_out *scsi_cmd; scsi_cmd = (struct scsi_security_protocol_out *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SECURITY_PROTOCOL_OUT; scsi_cmd->security_protocol = security_protocol; scsi_ulto2b(security_protocol_specific, scsi_cmd->security_protocol_specific); scsi_cmd->byte4 = byte4; scsi_ulto4b(dxfer_len, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_report_supported_opcodes(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, int options, int req_opcode, int req_service_action, uint8_t *data_ptr, uint32_t dxfer_len, int sense_len, int timeout) { struct scsi_report_supported_opcodes *scsi_cmd; scsi_cmd = (struct scsi_report_supported_opcodes *) &csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MAINTENANCE_IN; scsi_cmd->service_action = REPORT_SUPPORTED_OPERATION_CODES; scsi_cmd->options = options; scsi_cmd->requested_opcode = req_opcode; scsi_ulto2b(req_service_action, scsi_cmd->requested_service_action); scsi_ulto4b(dxfer_len, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } /* * Try make as good a match as possible with * available sub drivers */ int scsi_inquiry_match(caddr_t inqbuffer, caddr_t table_entry) { struct scsi_inquiry_pattern *entry; struct scsi_inquiry_data *inq; entry = (struct scsi_inquiry_pattern *)table_entry; inq = (struct scsi_inquiry_data *)inqbuffer; if (((SID_TYPE(inq) == entry->type) || (entry->type == T_ANY)) && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE : entry->media_type & SIP_MEDIA_FIXED) && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0) && (cam_strmatch(inq->product, entry->product, sizeof(inq->product)) == 0) && (cam_strmatch(inq->revision, entry->revision, sizeof(inq->revision)) == 0)) { return (0); } return (-1); } /* * Try make as good a match as possible with * available sub drivers */ int scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry) { struct scsi_static_inquiry_pattern *entry; struct scsi_inquiry_data *inq; entry = (struct scsi_static_inquiry_pattern *)table_entry; inq = (struct scsi_inquiry_data *)inqbuffer; if (((SID_TYPE(inq) == entry->type) || (entry->type == T_ANY)) && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE : entry->media_type & SIP_MEDIA_FIXED) && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0) && (cam_strmatch(inq->product, entry->product, sizeof(inq->product)) == 0) && (cam_strmatch(inq->revision, entry->revision, sizeof(inq->revision)) == 0)) { return (0); } return (-1); } /** * Compare two buffers of vpd device descriptors for a match. * * \param lhs Pointer to first buffer of descriptors to compare. * \param lhs_len The length of the first buffer. * \param rhs Pointer to second buffer of descriptors to compare. * \param rhs_len The length of the second buffer. * * \return 0 on a match, -1 otherwise. * * Treat rhs and lhs as arrays of vpd device id descriptors. Walk lhs matching * against each element in rhs until all data are exhausted or we have found * a match. */ int scsi_devid_match(uint8_t *lhs, size_t lhs_len, uint8_t *rhs, size_t rhs_len) { struct scsi_vpd_id_descriptor *lhs_id; struct scsi_vpd_id_descriptor *lhs_last; struct scsi_vpd_id_descriptor *rhs_last; uint8_t *lhs_end; uint8_t *rhs_end; lhs_end = lhs + lhs_len; rhs_end = rhs + rhs_len; /* * rhs_last and lhs_last are the last posible position of a valid * descriptor assuming it had a zero length identifier. We use * these variables to insure we can safely dereference the length * field in our loop termination tests. */ lhs_last = (struct scsi_vpd_id_descriptor *) (lhs_end - __offsetof(struct scsi_vpd_id_descriptor, identifier)); rhs_last = (struct scsi_vpd_id_descriptor *) (rhs_end - __offsetof(struct scsi_vpd_id_descriptor, identifier)); lhs_id = (struct scsi_vpd_id_descriptor *)lhs; while (lhs_id <= lhs_last && (lhs_id->identifier + lhs_id->length) <= lhs_end) { struct scsi_vpd_id_descriptor *rhs_id; rhs_id = (struct scsi_vpd_id_descriptor *)rhs; while (rhs_id <= rhs_last && (rhs_id->identifier + rhs_id->length) <= rhs_end) { if ((rhs_id->id_type & (SVPD_ID_ASSOC_MASK | SVPD_ID_TYPE_MASK)) == (lhs_id->id_type & (SVPD_ID_ASSOC_MASK | SVPD_ID_TYPE_MASK)) && rhs_id->length == lhs_id->length && memcmp(rhs_id->identifier, lhs_id->identifier, rhs_id->length) == 0) return (0); rhs_id = (struct scsi_vpd_id_descriptor *) (rhs_id->identifier + rhs_id->length); } lhs_id = (struct scsi_vpd_id_descriptor *) (lhs_id->identifier + lhs_id->length); } return (-1); } #ifdef _KERNEL int scsi_vpd_supported_page(struct cam_periph *periph, uint8_t page_id) { struct cam_ed *device; struct scsi_vpd_supported_pages *vpds; int i, num_pages; device = periph->path->device; vpds = (struct scsi_vpd_supported_pages *)device->supported_vpds; if (vpds != NULL) { num_pages = device->supported_vpds_len - SVPD_SUPPORTED_PAGES_HDR_LEN; for (i = 0; i < num_pages; i++) { if (vpds->page_list[i] == page_id) return (1); } } return (0); } static void init_scsi_delay(void) { int delay; delay = SCSI_DELAY; TUNABLE_INT_FETCH("kern.cam.scsi_delay", &delay); if (set_scsi_delay(delay) != 0) { printf("cam: invalid value for tunable kern.cam.scsi_delay\n"); set_scsi_delay(SCSI_DELAY); } } SYSINIT(scsi_delay, SI_SUB_TUNABLES, SI_ORDER_ANY, init_scsi_delay, NULL); static int sysctl_scsi_delay(SYSCTL_HANDLER_ARGS) { int error, delay; delay = scsi_delay; error = sysctl_handle_int(oidp, &delay, 0, req); if (error != 0 || req->newptr == NULL) return (error); return (set_scsi_delay(delay)); } SYSCTL_PROC(_kern_cam, OID_AUTO, scsi_delay, CTLTYPE_INT|CTLFLAG_RW, 0, 0, sysctl_scsi_delay, "I", "Delay to allow devices to settle after a SCSI bus reset (ms)"); static int set_scsi_delay(int delay) { /* * If someone sets this to 0, we assume that they want the * minimum allowable bus settle delay. */ if (delay == 0) { printf("cam: using minimum scsi_delay (%dms)\n", SCSI_MIN_DELAY); delay = SCSI_MIN_DELAY; } if (delay < SCSI_MIN_DELAY) return (EINVAL); scsi_delay = delay; return (0); } #endif /* _KERNEL */ Index: projects/clang700-import/sys/compat/linuxkpi/common/include/asm/atomic-long.h =================================================================== --- projects/clang700-import/sys/compat/linuxkpi/common/include/asm/atomic-long.h (revision 341812) +++ projects/clang700-import/sys/compat/linuxkpi/common/include/asm/atomic-long.h (revision 341813) @@ -1,127 +1,119 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013-2017 Mellanox Technologies, Ltd. * 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 _ATOMIC_LONG_H_ #define _ATOMIC_LONG_H_ #include #include #include #define ATOMIC_LONG_INIT(x) { .counter = (x) } typedef struct { volatile long counter; } atomic_long_t; #define atomic_long_add(i, v) atomic_long_add_return((i), (v)) #define atomic_long_inc_return(v) atomic_long_add_return(1, (v)) #define atomic_long_inc_not_zero(v) atomic_long_add_unless((v), 1, 0) static inline long atomic_long_add_return(long i, atomic_long_t *v) { return i + atomic_fetchadd_long(&v->counter, i); } static inline void atomic_long_set(atomic_long_t *v, long i) { WRITE_ONCE(v->counter, i); } static inline long atomic_long_read(atomic_long_t *v) { return READ_ONCE(v->counter); } static inline long atomic_long_inc(atomic_long_t *v) { return atomic_fetchadd_long(&v->counter, 1) + 1; } static inline long atomic_long_dec(atomic_long_t *v) { return atomic_fetchadd_long(&v->counter, -1) - 1; } static inline long atomic_long_xchg(atomic_long_t *v, long val) { -#if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) return atomic_swap_long(&v->counter, val); -#else - long ret = atomic_long_read(v); - - while (!atomic_fcmpset_long(&v->counter, &ret, val)) - ; - return (ret); -#endif } static inline long atomic_long_cmpxchg(atomic_long_t *v, long old, long new) { long ret = old; for (;;) { if (atomic_fcmpset_long(&v->counter, &ret, new)) break; if (ret != old) break; } return (ret); } static inline int atomic_long_add_unless(atomic_long_t *v, long a, long u) { long c = atomic_long_read(v); for (;;) { if (unlikely(c == u)) break; if (likely(atomic_fcmpset_long(&v->counter, &c, c + a))) break; } return (c != u); } static inline long atomic_long_dec_and_test(atomic_long_t *v) { long i = atomic_long_add(-1, v); return i == 0 ; } #endif /* _ATOMIC_LONG_H_ */ Index: projects/clang700-import/sys/compat/linuxkpi/common/include/asm/atomic.h =================================================================== --- projects/clang700-import/sys/compat/linuxkpi/common/include/asm/atomic.h (revision 341812) +++ projects/clang700-import/sys/compat/linuxkpi/common/include/asm/atomic.h (revision 341813) @@ -1,315 +1,307 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013-2018 Mellanox Technologies, Ltd. * 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 _ASM_ATOMIC_H_ #define _ASM_ATOMIC_H_ #include #include #include #define ATOMIC_INIT(x) { .counter = (x) } typedef struct { volatile int counter; } atomic_t; /*------------------------------------------------------------------------* * 32-bit atomic operations *------------------------------------------------------------------------*/ #define atomic_add(i, v) atomic_add_return((i), (v)) #define atomic_sub(i, v) atomic_sub_return((i), (v)) #define atomic_inc_return(v) atomic_add_return(1, (v)) #define atomic_add_negative(i, v) (atomic_add_return((i), (v)) < 0) #define atomic_add_and_test(i, v) (atomic_add_return((i), (v)) == 0) #define atomic_sub_and_test(i, v) (atomic_sub_return((i), (v)) == 0) #define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0) #define atomic_inc_and_test(v) (atomic_add_return(1, (v)) == 0) #define atomic_dec_return(v) atomic_sub_return(1, (v)) #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) static inline int atomic_add_return(int i, atomic_t *v) { return i + atomic_fetchadd_int(&v->counter, i); } static inline int atomic_sub_return(int i, atomic_t *v) { return atomic_fetchadd_int(&v->counter, -i) - i; } static inline void atomic_set(atomic_t *v, int i) { WRITE_ONCE(v->counter, i); } static inline void atomic_set_release(atomic_t *v, int i) { atomic_store_rel_int(&v->counter, i); } static inline void atomic_set_mask(unsigned int mask, atomic_t *v) { atomic_set_int(&v->counter, mask); } static inline int atomic_read(const atomic_t *v) { return READ_ONCE(v->counter); } static inline int atomic_inc(atomic_t *v) { return atomic_fetchadd_int(&v->counter, 1) + 1; } static inline int atomic_dec(atomic_t *v) { return atomic_fetchadd_int(&v->counter, -1) - 1; } static inline int atomic_add_unless(atomic_t *v, int a, int u) { int c = atomic_read(v); for (;;) { if (unlikely(c == u)) break; if (likely(atomic_fcmpset_int(&v->counter, &c, c + a))) break; } return (c != u); } static inline void atomic_clear_mask(unsigned int mask, atomic_t *v) { atomic_clear_int(&v->counter, mask); } static inline int atomic_xchg(atomic_t *v, int i) { -#if !defined(__mips__) return (atomic_swap_int(&v->counter, i)); -#else - int ret = atomic_read(v); - - while (!atomic_fcmpset_int(&v->counter, &ret, i)) - ; - return (ret); -#endif } static inline int atomic_cmpxchg(atomic_t *v, int old, int new) { int ret = old; for (;;) { if (atomic_fcmpset_int(&v->counter, &ret, new)) break; if (ret != old) break; } return (ret); } #if defined(__amd64__) || defined(__arm64__) || defined(__i386__) #define LINUXKPI_ATOMIC_8(...) __VA_ARGS__ #define LINUXKPI_ATOMIC_16(...) __VA_ARGS__ #else #define LINUXKPI_ATOMIC_8(...) #define LINUXKPI_ATOMIC_16(...) #endif #if !(defined(i386) || (defined(__mips__) && !(defined(__mips_n32) || \ defined(__mips_n64))) || (defined(__powerpc__) && \ !defined(__powerpc64__))) #define LINUXKPI_ATOMIC_64(...) __VA_ARGS__ #else #define LINUXKPI_ATOMIC_64(...) #endif #define cmpxchg(ptr, old, new) ({ \ union { \ __typeof(*(ptr)) val; \ u8 u8[0]; \ u16 u16[0]; \ u32 u32[0]; \ u64 u64[0]; \ } __ret = { .val = (old) }, __new = { .val = (new) }; \ \ CTASSERT( \ LINUXKPI_ATOMIC_8(sizeof(__ret.val) == 1 ||) \ LINUXKPI_ATOMIC_16(sizeof(__ret.val) == 2 ||) \ LINUXKPI_ATOMIC_64(sizeof(__ret.val) == 8 ||) \ sizeof(__ret.val) == 4); \ \ switch (sizeof(__ret.val)) { \ LINUXKPI_ATOMIC_8( \ case 1: \ while (!atomic_fcmpset_8((volatile u8 *)(ptr), \ __ret.u8, __new.u8[0]) && __ret.val == (old)) \ ; \ break; \ ) \ LINUXKPI_ATOMIC_16( \ case 2: \ while (!atomic_fcmpset_16((volatile u16 *)(ptr), \ __ret.u16, __new.u16[0]) && __ret.val == (old)) \ ; \ break; \ ) \ case 4: \ while (!atomic_fcmpset_32((volatile u32 *)(ptr), \ __ret.u32, __new.u32[0]) && __ret.val == (old)) \ ; \ break; \ LINUXKPI_ATOMIC_64( \ case 8: \ while (!atomic_fcmpset_64((volatile u64 *)(ptr), \ __ret.u64, __new.u64[0]) && __ret.val == (old)) \ ; \ break; \ ) \ } \ __ret.val; \ }) #define cmpxchg_relaxed(...) cmpxchg(__VA_ARGS__) #define xchg(ptr, new) ({ \ union { \ __typeof(*(ptr)) val; \ u8 u8[0]; \ u16 u16[0]; \ u32 u32[0]; \ u64 u64[0]; \ } __ret, __new = { .val = (new) }; \ \ CTASSERT( \ LINUXKPI_ATOMIC_8(sizeof(__ret.val) == 1 ||) \ LINUXKPI_ATOMIC_16(sizeof(__ret.val) == 2 ||) \ LINUXKPI_ATOMIC_64(sizeof(__ret.val) == 8 ||) \ sizeof(__ret.val) == 4); \ \ switch (sizeof(__ret.val)) { \ LINUXKPI_ATOMIC_8( \ case 1: \ __ret.val = READ_ONCE(*ptr); \ while (!atomic_fcmpset_8((volatile u8 *)(ptr), \ __ret.u8, __new.u8[0])) \ ; \ break; \ ) \ LINUXKPI_ATOMIC_16( \ case 2: \ __ret.val = READ_ONCE(*ptr); \ while (!atomic_fcmpset_16((volatile u16 *)(ptr), \ __ret.u16, __new.u16[0])) \ ; \ break; \ ) \ case 4: \ __ret.u32[0] = atomic_swap_32((volatile u32 *)(ptr), \ __new.u32[0]); \ break; \ LINUXKPI_ATOMIC_64( \ case 8: \ __ret.u64[0] = atomic_swap_64((volatile u64 *)(ptr), \ __new.u64[0]); \ break; \ ) \ } \ __ret.val; \ }) static inline int atomic_dec_if_positive(atomic_t *v) { int retval; int old; old = atomic_read(v); for (;;) { retval = old - 1; if (unlikely(retval < 0)) break; if (likely(atomic_fcmpset_int(&v->counter, &old, retval))) break; } return (retval); } #define LINUX_ATOMIC_OP(op, c_op) \ static inline void atomic_##op(int i, atomic_t *v) \ { \ int c, old; \ \ c = v->counter; \ while ((old = atomic_cmpxchg(v, c, c c_op i)) != c) \ c = old; \ } #define LINUX_ATOMIC_FETCH_OP(op, c_op) \ static inline int atomic_fetch_##op(int i, atomic_t *v) \ { \ int c, old; \ \ c = v->counter; \ while ((old = atomic_cmpxchg(v, c, c c_op i)) != c) \ c = old; \ \ return (c); \ } LINUX_ATOMIC_OP(or, |) LINUX_ATOMIC_OP(and, &) LINUX_ATOMIC_OP(andnot, &~) LINUX_ATOMIC_OP(xor, ^) LINUX_ATOMIC_FETCH_OP(or, |) LINUX_ATOMIC_FETCH_OP(and, &) LINUX_ATOMIC_FETCH_OP(andnot, &~) LINUX_ATOMIC_FETCH_OP(xor, ^) #endif /* _ASM_ATOMIC_H_ */ Index: projects/clang700-import/sys/dev/ahci/ahci_pci.c =================================================================== --- projects/clang700-import/sys/dev/ahci/ahci_pci.c (revision 341812) +++ projects/clang700-import/sys/dev/ahci/ahci_pci.c (revision 341813) @@ -1,691 +1,688 @@ /*- * Copyright (c) 2009-2012 Alexander Motin * 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. 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 #include #include #include #include #include #include #include "ahci.h" static int force_ahci = 1; TUNABLE_INT("hw.ahci.force", &force_ahci); static const struct { uint32_t id; uint8_t rev; const char *name; int quirks; } ahci_ids[] = { {0x43801002, 0x00, "AMD SB600", AHCI_Q_NOMSI | AHCI_Q_ATI_PMP_BUG | AHCI_Q_MAXIO_64K}, {0x43901002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG | AHCI_Q_1MSI}, {0x43911002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG | AHCI_Q_1MSI}, {0x43921002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG | AHCI_Q_1MSI}, {0x43931002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG | AHCI_Q_1MSI}, {0x43941002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG | AHCI_Q_1MSI}, /* Not sure SB8x0/SB9x0 needs this quirk. Be conservative though */ {0x43951002, 0x00, "AMD SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG}, {0x43b61022, 0x00, "AMD X399", 0}, {0x43b51022, 0x00, "AMD 300 Series", 0}, /* X370 */ {0x43b71022, 0x00, "AMD 300 Series", 0}, /* B350 */ {0x78001022, 0x00, "AMD Hudson-2", 0}, {0x78011022, 0x00, "AMD Hudson-2", 0}, {0x78021022, 0x00, "AMD Hudson-2", 0}, {0x78031022, 0x00, "AMD Hudson-2", 0}, {0x78041022, 0x00, "AMD Hudson-2", 0}, {0x79001022, 0x00, "AMD KERNCZ", 0}, {0x79011022, 0x00, "AMD KERNCZ", 0}, {0x79021022, 0x00, "AMD KERNCZ", 0}, {0x79031022, 0x00, "AMD KERNCZ", 0}, {0x79041022, 0x00, "AMD KERNCZ", 0}, {0x06011b21, 0x00, "ASMedia ASM1060", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x06021b21, 0x00, "ASMedia ASM1060", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x06111b21, 0x00, "ASMedia ASM1061", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x06121b21, 0x00, "ASMedia ASM1062", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x06201b21, 0x00, "ASMedia ASM106x", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x06211b21, 0x00, "ASMedia ASM106x", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x06221b21, 0x00, "ASMedia ASM106x", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x06241b21, 0x00, "ASMedia ASM106x", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x06251b21, 0x00, "ASMedia ASM106x", AHCI_Q_NOCCS|AHCI_Q_NOAUX}, {0x26528086, 0x00, "Intel ICH6", AHCI_Q_NOFORCE}, {0x26538086, 0x00, "Intel ICH6M", AHCI_Q_NOFORCE}, {0x26818086, 0x00, "Intel ESB2", 0}, {0x26828086, 0x00, "Intel ESB2", 0}, {0x26838086, 0x00, "Intel ESB2", 0}, {0x27c18086, 0x00, "Intel ICH7", 0}, {0x27c38086, 0x00, "Intel ICH7", 0}, {0x27c58086, 0x00, "Intel ICH7M", 0}, {0x27c68086, 0x00, "Intel ICH7M", 0}, {0x28218086, 0x00, "Intel ICH8", 0}, {0x28228086, 0x00, "Intel ICH8+ (RAID)", 0}, {0x28248086, 0x00, "Intel ICH8", 0}, {0x28298086, 0x00, "Intel ICH8M", 0}, {0x282a8086, 0x00, "Intel ICH8M+ (RAID)", 0}, {0x29228086, 0x00, "Intel ICH9", 0}, {0x29238086, 0x00, "Intel ICH9", 0}, {0x29248086, 0x00, "Intel ICH9", 0}, {0x29258086, 0x00, "Intel ICH9", 0}, {0x29278086, 0x00, "Intel ICH9", 0}, {0x29298086, 0x00, "Intel ICH9M", 0}, {0x292a8086, 0x00, "Intel ICH9M", 0}, {0x292b8086, 0x00, "Intel ICH9M", 0}, {0x292c8086, 0x00, "Intel ICH9M", 0}, {0x292f8086, 0x00, "Intel ICH9M", 0}, {0x294d8086, 0x00, "Intel ICH9", 0}, {0x294e8086, 0x00, "Intel ICH9M", 0}, {0x3a058086, 0x00, "Intel ICH10 (RAID)", 0}, {0x3a228086, 0x00, "Intel ICH10", 0}, {0x3a258086, 0x00, "Intel ICH10 (RAID)", 0}, {0x3b228086, 0x00, "Intel Ibex Peak", 0}, {0x3b238086, 0x00, "Intel Ibex Peak", 0}, {0x3b258086, 0x00, "Intel Ibex Peak (RAID)", 0}, {0x3b298086, 0x00, "Intel Ibex Peak-M", 0}, {0x3b2c8086, 0x00, "Intel Ibex Peak-M (RAID)", 0}, {0x3b2f8086, 0x00, "Intel Ibex Peak-M", 0}, {0x19b08086, 0x00, "Intel Denverton", 0}, {0x19b18086, 0x00, "Intel Denverton", 0}, {0x19b28086, 0x00, "Intel Denverton", 0}, {0x19b38086, 0x00, "Intel Denverton", 0}, {0x19b48086, 0x00, "Intel Denverton", 0}, {0x19b58086, 0x00, "Intel Denverton", 0}, {0x19b68086, 0x00, "Intel Denverton", 0}, {0x19b78086, 0x00, "Intel Denverton", 0}, {0x19be8086, 0x00, "Intel Denverton", 0}, {0x19bf8086, 0x00, "Intel Denverton", 0}, {0x19c08086, 0x00, "Intel Denverton", 0}, {0x19c18086, 0x00, "Intel Denverton", 0}, {0x19c28086, 0x00, "Intel Denverton", 0}, {0x19c38086, 0x00, "Intel Denverton", 0}, {0x19c48086, 0x00, "Intel Denverton", 0}, {0x19c58086, 0x00, "Intel Denverton", 0}, {0x19c68086, 0x00, "Intel Denverton", 0}, {0x19c78086, 0x00, "Intel Denverton", 0}, {0x19ce8086, 0x00, "Intel Denverton", 0}, {0x19cf8086, 0x00, "Intel Denverton", 0}, {0x1c028086, 0x00, "Intel Cougar Point", 0}, {0x1c038086, 0x00, "Intel Cougar Point", 0}, {0x1c048086, 0x00, "Intel Cougar Point (RAID)", 0}, {0x1c058086, 0x00, "Intel Cougar Point (RAID)", 0}, {0x1c068086, 0x00, "Intel Cougar Point (RAID)", 0}, {0x1d028086, 0x00, "Intel Patsburg", 0}, {0x1d048086, 0x00, "Intel Patsburg", 0}, {0x1d068086, 0x00, "Intel Patsburg", 0}, {0x28268086, 0x00, "Intel Patsburg+ (RAID)", 0}, {0x1e028086, 0x00, "Intel Panther Point", 0}, {0x1e038086, 0x00, "Intel Panther Point", 0}, {0x1e048086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e058086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e068086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e078086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e0e8086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e0f8086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1f228086, 0x00, "Intel Avoton", 0}, {0x1f238086, 0x00, "Intel Avoton", 0}, {0x1f248086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f258086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f268086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f278086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f2e8086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f2f8086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f328086, 0x00, "Intel Avoton", 0}, {0x1f338086, 0x00, "Intel Avoton", 0}, {0x1f348086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f358086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f368086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f378086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f3e8086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f3f8086, 0x00, "Intel Avoton (RAID)", 0}, {0x23a38086, 0x00, "Intel Coleto Creek", 0}, {0x8c028086, 0x00, "Intel Lynx Point", 0}, {0x8c038086, 0x00, "Intel Lynx Point", 0}, {0x8c048086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c058086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c068086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c078086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c0e8086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c0f8086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c828086, 0x00, "Intel Wildcat Point", 0}, {0x8c838086, 0x00, "Intel Wildcat Point", 0}, {0x8c848086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c858086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c868086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c878086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c8e8086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c8f8086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8d028086, 0x00, "Intel Wellsburg", 0}, {0x8d048086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8d068086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8d628086, 0x00, "Intel Wellsburg", 0}, {0x8d648086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8d668086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8d6e8086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x28238086, 0x00, "Intel Wellsburg+ (RAID)", 0}, {0x28278086, 0x00, "Intel Wellsburg+ (RAID)", 0}, {0x9c028086, 0x00, "Intel Lynx Point-LP", 0}, {0x9c038086, 0x00, "Intel Lynx Point-LP", 0}, {0x9c048086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c058086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c068086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c078086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c0e8086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c0f8086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9d038086, 0x00, "Intel Sunrise Point-LP", 0}, {0x9d058086, 0x00, "Intel Sunrise Point-LP (RAID)", 0}, {0x9d078086, 0x00, "Intel Sunrise Point-LP (RAID)", 0}, {0xa1028086, 0x00, "Intel Sunrise Point", 0}, {0xa1038086, 0x00, "Intel Sunrise Point", 0}, {0xa1058086, 0x00, "Intel Sunrise Point (RAID)", 0}, {0xa1068086, 0x00, "Intel Sunrise Point (RAID)", 0}, {0xa1078086, 0x00, "Intel Sunrise Point (RAID)", 0}, {0xa10f8086, 0x00, "Intel Sunrise Point (RAID)", 0}, {0xa1828086, 0x00, "Intel Lewisburg", 0}, {0xa1868086, 0x00, "Intel Lewisburg (RAID)", 0}, {0xa1d28086, 0x00, "Intel Lewisburg", 0}, {0xa1d68086, 0x00, "Intel Lewisburg (RAID)", 0}, {0xa2028086, 0x00, "Intel Lewisburg", 0}, {0xa2068086, 0x00, "Intel Lewisburg (RAID)", 0}, {0xa2528086, 0x00, "Intel Lewisburg", 0}, {0xa2568086, 0x00, "Intel Lewisburg (RAID)", 0}, {0xa2828086, 0x00, "Intel Union Point", 0}, {0xa2868086, 0x00, "Intel Union Point (RAID)", 0}, {0xa28e8086, 0x00, "Intel Union Point (RAID)", 0}, {0x23238086, 0x00, "Intel DH89xxCC", 0}, {0x2360197b, 0x00, "JMicron JMB360", 0}, {0x2361197b, 0x00, "JMicron JMB361", AHCI_Q_NOFORCE | AHCI_Q_1CH}, {0x2362197b, 0x00, "JMicron JMB362", 0}, {0x2363197b, 0x00, "JMicron JMB363", AHCI_Q_NOFORCE}, {0x2365197b, 0x00, "JMicron JMB365", AHCI_Q_NOFORCE}, {0x2366197b, 0x00, "JMicron JMB366", AHCI_Q_NOFORCE}, {0x2368197b, 0x00, "JMicron JMB368", AHCI_Q_NOFORCE}, {0x611111ab, 0x00, "Marvell 88SE6111", AHCI_Q_NOFORCE | AHCI_Q_NOPMP | AHCI_Q_1CH | AHCI_Q_EDGEIS}, {0x612111ab, 0x00, "Marvell 88SE6121", AHCI_Q_NOFORCE | AHCI_Q_NOPMP | AHCI_Q_2CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x614111ab, 0x00, "Marvell 88SE6141", AHCI_Q_NOFORCE | AHCI_Q_NOPMP | AHCI_Q_4CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x614511ab, 0x00, "Marvell 88SE6145", AHCI_Q_NOFORCE | AHCI_Q_NOPMP | AHCI_Q_4CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x91201b4b, 0x00, "Marvell 88SE912x", AHCI_Q_EDGEIS}, {0x91231b4b, 0x11, "Marvell 88SE912x", AHCI_Q_ALTSIG}, {0x91231b4b, 0x00, "Marvell 88SE912x", AHCI_Q_EDGEIS|AHCI_Q_SATA2}, {0x91251b4b, 0x00, "Marvell 88SE9125", 0}, {0x91281b4b, 0x00, "Marvell 88SE9128", AHCI_Q_ALTSIG}, {0x91301b4b, 0x00, "Marvell 88SE9130", AHCI_Q_ALTSIG}, {0x91721b4b, 0x00, "Marvell 88SE9172", 0}, {0x91821b4b, 0x00, "Marvell 88SE9182", 0}, {0x91831b4b, 0x00, "Marvell 88SS9183", 0}, {0x91a01b4b, 0x00, "Marvell 88SE91Ax", 0}, {0x92151b4b, 0x00, "Marvell 88SE9215", 0}, {0x92201b4b, 0x00, "Marvell 88SE9220", AHCI_Q_ALTSIG}, {0x92301b4b, 0x00, "Marvell 88SE9230", AHCI_Q_ALTSIG}, {0x92351b4b, 0x00, "Marvell 88SE9235", 0}, {0x06201103, 0x00, "HighPoint RocketRAID 620", 0}, {0x06201b4b, 0x00, "HighPoint RocketRAID 620", 0}, {0x06221103, 0x00, "HighPoint RocketRAID 622", 0}, {0x06221b4b, 0x00, "HighPoint RocketRAID 622", 0}, {0x06401103, 0x00, "HighPoint RocketRAID 640", 0}, {0x06401b4b, 0x00, "HighPoint RocketRAID 640", 0}, {0x06441103, 0x00, "HighPoint RocketRAID 644", 0}, {0x06441b4b, 0x00, "HighPoint RocketRAID 644", 0}, {0x06411103, 0x00, "HighPoint RocketRAID 640L", 0}, {0x06421103, 0x00, "HighPoint RocketRAID 642L", 0}, {0x06451103, 0x00, "HighPoint RocketRAID 644L", 0}, {0x044c10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044d10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044e10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044f10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045c10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045d10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045e10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045f10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x055010de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055110de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055210de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055310de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055410de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055510de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055610de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055710de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055810de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055910de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055A10de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055B10de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x058410de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x07f010de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f110de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f210de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f310de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f410de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f510de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f610de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f710de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f810de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f910de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07fa10de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07fb10de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x0ad010de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad110de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad210de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad310de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad410de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad510de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad610de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad710de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad810de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad910de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ada10de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0adb10de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ab410de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab510de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab610de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab710de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab810de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab910de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0aba10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abb10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abc10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abd10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abe10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abf10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0d8410de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8510de, 0x00, "NVIDIA MCP89", AHCI_Q_NOFORCE|AHCI_Q_NOAA}, {0x0d8610de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8710de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8810de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8910de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8a10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8b10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8c10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8d10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8e10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8f10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x3781105a, 0x00, "Promise TX8660", 0}, {0x33491106, 0x00, "VIA VT8251", AHCI_Q_NOPMP|AHCI_Q_NONCQ}, {0x62871106, 0x00, "VIA VT8251", AHCI_Q_NOPMP|AHCI_Q_NONCQ}, {0x11841039, 0x00, "SiS 966", 0}, {0x11851039, 0x00, "SiS 968", 0}, {0x01861039, 0x00, "SiS 968", 0}, {0xa01c177d, 0x00, "ThunderX", AHCI_Q_ABAR0|AHCI_Q_1MSI}, {0x00311c36, 0x00, "Annapurna", AHCI_Q_FORCE_PI|AHCI_Q_RESTORE_CAP|AHCI_Q_NOMSIX}, {0x1600144d, 0x00, "Samsung", AHCI_Q_NOMSI}, {0x00000000, 0x00, NULL, 0} }; static int ahci_pci_ctlr_reset(device_t dev) { - if (pci_read_config(dev, PCIR_DEVVENDOR, 4) == 0x28298086 && - (pci_read_config(dev, 0x92, 1) & 0xfe) == 0x04) - pci_write_config(dev, 0x92, 0x01, 1); - return ahci_ctlr_reset(dev); + return(ahci_ctlr_reset(dev)); } static int ahci_probe(device_t dev) { char buf[64]; int i, valid = 0; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); /* * Ensure it is not a PCI bridge (some vendors use * the same PID and VID in PCI bridge and AHCI cards). */ if (pci_get_class(dev) == PCIC_BRIDGE) return (ENXIO); /* Is this a possible AHCI candidate? */ if (pci_get_class(dev) == PCIC_STORAGE && pci_get_subclass(dev) == PCIS_STORAGE_SATA && pci_get_progif(dev) == PCIP_STORAGE_SATA_AHCI_1_0) valid = 1; else if (pci_get_class(dev) == PCIC_STORAGE && pci_get_subclass(dev) == PCIS_STORAGE_RAID) valid = 2; /* Is this a known AHCI chip? */ for (i = 0; ahci_ids[i].id != 0; i++) { if (ahci_ids[i].id == devid && ahci_ids[i].rev <= revid && (valid || (force_ahci == 1 && !(ahci_ids[i].quirks & AHCI_Q_NOFORCE)))) { /* Do not attach JMicrons with single PCI function. */ if (pci_get_vendor(dev) == 0x197b && (pci_read_config(dev, 0xdf, 1) & 0x40) == 0) return (ENXIO); snprintf(buf, sizeof(buf), "%s AHCI SATA controller", ahci_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_DEFAULT); } } if (valid != 1) return (ENXIO); device_set_desc_copy(dev, "AHCI SATA controller"); return (BUS_PROBE_DEFAULT); } static int ahci_ata_probe(device_t dev) { char buf[64]; int i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); if ((intptr_t)device_get_ivars(dev) >= 0) return (ENXIO); /* Is this a known AHCI chip? */ for (i = 0; ahci_ids[i].id != 0; i++) { if (ahci_ids[i].id == devid && ahci_ids[i].rev <= revid) { snprintf(buf, sizeof(buf), "%s AHCI SATA controller", ahci_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_DEFAULT); } } device_set_desc_copy(dev, "AHCI SATA controller"); return (BUS_PROBE_DEFAULT); } static int ahci_pci_read_msix_bars(device_t dev, uint8_t *table_bar, uint8_t *pba_bar) { int cap_offset = 0, ret; uint32_t val; if ((table_bar == NULL) || (pba_bar == NULL)) return (EINVAL); ret = pci_find_cap(dev, PCIY_MSIX, &cap_offset); if (ret != 0) return (EINVAL); val = pci_read_config(dev, cap_offset + PCIR_MSIX_TABLE, 4); *table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); val = pci_read_config(dev, cap_offset + PCIR_MSIX_PBA, 4); *pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); return (0); } static int ahci_pci_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int error, i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); int msi_count, msix_count; uint8_t table_bar = 0, pba_bar = 0; msi_count = pci_msi_count(dev); msix_count = pci_msix_count(dev); i = 0; while (ahci_ids[i].id != 0 && (ahci_ids[i].id != devid || ahci_ids[i].rev > revid)) i++; ctlr->quirks = ahci_ids[i].quirks; /* Limit speed for my onboard JMicron external port. * It is not eSATA really, limit to SATA 1 */ if (pci_get_devid(dev) == 0x2363197b && pci_get_subvendor(dev) == 0x1043 && pci_get_subdevice(dev) == 0x81e4) ctlr->quirks |= AHCI_Q_SATA1_UNIT0; resource_int_value(device_get_name(dev), device_get_unit(dev), "quirks", &ctlr->quirks); ctlr->vendorid = pci_get_vendor(dev); ctlr->deviceid = pci_get_device(dev); ctlr->subvendorid = pci_get_subvendor(dev); ctlr->subdeviceid = pci_get_subdevice(dev); /* Default AHCI Base Address is BAR(5), Cavium uses BAR(0) */ if (ctlr->quirks & AHCI_Q_ABAR0) ctlr->r_rid = PCIR_BAR(0); else ctlr->r_rid = PCIR_BAR(5); if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE))) return ENXIO; if (ctlr->quirks & AHCI_Q_NOMSIX) msix_count = 0; /* Read MSI-x BAR IDs if supported */ if (msix_count > 0) { error = ahci_pci_read_msix_bars(dev, &table_bar, &pba_bar); if (error == 0) { ctlr->r_msix_tab_rid = table_bar; ctlr->r_msix_pba_rid = pba_bar; } else { /* Failed to read BARs, disable MSI-x */ msix_count = 0; } } /* Allocate resources for MSI-x table and PBA */ if (msix_count > 0) { /* * Allocate new MSI-x table only if not * allocated before. */ ctlr->r_msix_table = NULL; if (ctlr->r_msix_tab_rid != ctlr->r_rid) { /* Separate BAR for MSI-x */ ctlr->r_msix_table = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_msix_tab_rid, RF_ACTIVE); if (ctlr->r_msix_table == NULL) { ahci_free_mem(dev); return (ENXIO); } } /* * Allocate new PBA table only if not * allocated before. */ ctlr->r_msix_pba = NULL; if ((ctlr->r_msix_pba_rid != ctlr->r_msix_tab_rid) && (ctlr->r_msix_pba_rid != ctlr->r_rid)) { /* Separate BAR for PBA */ ctlr->r_msix_pba = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_msix_pba_rid, RF_ACTIVE); if (ctlr->r_msix_pba == NULL) { ahci_free_mem(dev); return (ENXIO); } } } pci_enable_busmaster(dev); /* Reset controller */ if ((error = ahci_pci_ctlr_reset(dev)) != 0) { ahci_free_mem(dev); return (error); } /* Setup interrupts. */ /* Setup MSI register parameters */ /* Process hints. */ if (ctlr->quirks & AHCI_Q_NOMSI) ctlr->msi = 0; else if (ctlr->quirks & AHCI_Q_1MSI) ctlr->msi = 1; else ctlr->msi = 2; resource_int_value(device_get_name(dev), device_get_unit(dev), "msi", &ctlr->msi); ctlr->numirqs = 1; if (msi_count == 0 && msix_count == 0) ctlr->msi = 0; if (ctlr->msi < 0) ctlr->msi = 0; else if (ctlr->msi == 1) { msi_count = min(1, msi_count); msix_count = min(1, msix_count); } else if (ctlr->msi > 1) ctlr->msi = 2; /* Allocate MSI/MSI-x if needed/present. */ if (ctlr->msi > 0) { error = ENXIO; /* Try to allocate MSI-x first */ if (msix_count > 0) { error = pci_alloc_msix(dev, &msix_count); if (error == 0) ctlr->numirqs = msix_count; } /* * Try to allocate MSI if msi_count is greater than 0 * and if MSI-x allocation failed. */ if ((error != 0) && (msi_count > 0)) { error = pci_alloc_msi(dev, &msi_count); if (error == 0) ctlr->numirqs = msi_count; } /* Both MSI and MSI-x allocations failed */ if (error != 0) { ctlr->msi = 0; device_printf(dev, "Failed to allocate MSI/MSI-x, " "falling back to INTx\n"); } } error = ahci_attach(dev); if (error != 0) { if (ctlr->msi > 0) pci_release_msi(dev); ahci_free_mem(dev); } return error; } static int ahci_pci_detach(device_t dev) { ahci_detach(dev); pci_release_msi(dev); return (0); } static int ahci_pci_suspend(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); bus_generic_suspend(dev); /* Disable interupts, so the state change(s) doesn't trigger */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, ATA_INL(ctlr->r_mem, AHCI_GHC) & (~AHCI_GHC_IE)); return 0; } static int ahci_pci_resume(device_t dev) { int res; if ((res = ahci_pci_ctlr_reset(dev)) != 0) return (res); ahci_ctlr_setup(dev); return (bus_generic_resume(dev)); } static device_method_t ahci_methods[] = { DEVMETHOD(device_probe, ahci_probe), DEVMETHOD(device_attach, ahci_pci_attach), DEVMETHOD(device_detach, ahci_pci_detach), DEVMETHOD(device_suspend, ahci_pci_suspend), DEVMETHOD(device_resume, ahci_pci_resume), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), DEVMETHOD(bus_child_location_str, ahci_child_location_str), DEVMETHOD(bus_get_dma_tag, ahci_get_dma_tag), DEVMETHOD_END }; static driver_t ahci_driver = { "ahci", ahci_methods, sizeof(struct ahci_controller) }; DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, NULL, NULL); /* Also matches class / subclass / progid XXX need to add when we have masking support */ MODULE_PNP_INFO("W32:vendor/device", pci, ahci, ahci_ids, nitems(ahci_ids) - 1); static device_method_t ahci_ata_methods[] = { DEVMETHOD(device_probe, ahci_ata_probe), DEVMETHOD(device_attach, ahci_pci_attach), DEVMETHOD(device_detach, ahci_pci_detach), DEVMETHOD(device_suspend, ahci_pci_suspend), DEVMETHOD(device_resume, ahci_pci_resume), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), DEVMETHOD(bus_child_location_str, ahci_child_location_str), DEVMETHOD_END }; static driver_t ahci_ata_driver = { "ahci", ahci_ata_methods, sizeof(struct ahci_controller) }; DRIVER_MODULE(ahci, atapci, ahci_ata_driver, ahci_devclass, NULL, NULL); Index: projects/clang700-import/sys/dev/md/md.c =================================================================== --- projects/clang700-import/sys/dev/md/md.c (revision 341812) +++ projects/clang700-import/sys/dev/md/md.c (revision 341813) @@ -1,2202 +1,2202 @@ /*- * SPDX-License-Identifier: (Beerware AND BSD-3-Clause) * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ /*- * The following functions are based in the vn(4) driver: mdstart_swap(), * mdstart_vnode(), mdcreate_swap(), mdcreate_vnode() and mddestroy(), * and as such under the following copyright: * * Copyright (c) 1988 University of Utah. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * 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, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: Utah Hdr: vn.c 1.13 94/04/02 * * from: @(#)vn.c 8.6 (Berkeley) 4/1/94 * From: src/sys/dev/vn/vn.c,v 1.122 2000/12/16 16:06:03 */ #include "opt_rootdevname.h" #include "opt_geom.h" #include "opt_md.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 #define MD_MODVER 1 #define MD_SHUTDOWN 0x10000 /* Tell worker thread to terminate. */ #define MD_EXITING 0x20000 /* Worker thread is exiting. */ #ifndef MD_NSECT #define MD_NSECT (10000 * 2) #endif struct md_req { unsigned md_unit; /* unit number */ enum md_types md_type; /* type of disk */ off_t md_mediasize; /* size of disk in bytes */ unsigned md_sectorsize; /* sectorsize */ unsigned md_options; /* options */ int md_fwheads; /* firmware heads */ int md_fwsectors; /* firmware sectors */ char *md_file; /* pathname of file to mount */ enum uio_seg md_file_seg; /* location of md_file */ char *md_label; /* label of the device (userspace) */ int *md_units; /* pointer to units array (kernel) */ size_t md_units_nitems; /* items in md_units array */ }; #ifdef COMPAT_FREEBSD32 struct md_ioctl32 { unsigned md_version; unsigned md_unit; enum md_types md_type; uint32_t md_file; off_t md_mediasize; unsigned md_sectorsize; unsigned md_options; uint64_t md_base; int md_fwheads; int md_fwsectors; uint32_t md_label; int md_pad[MDNPAD]; } __attribute__((__packed__)); CTASSERT((sizeof(struct md_ioctl32)) == 436); #define MDIOCATTACH_32 _IOC_NEWTYPE(MDIOCATTACH, struct md_ioctl32) #define MDIOCDETACH_32 _IOC_NEWTYPE(MDIOCDETACH, struct md_ioctl32) #define MDIOCQUERY_32 _IOC_NEWTYPE(MDIOCQUERY, struct md_ioctl32) #define MDIOCLIST_32 _IOC_NEWTYPE(MDIOCLIST, struct md_ioctl32) #define MDIOCRESIZE_32 _IOC_NEWTYPE(MDIOCRESIZE, struct md_ioctl32) #endif /* COMPAT_FREEBSD32 */ static MALLOC_DEFINE(M_MD, "md_disk", "Memory Disk"); static MALLOC_DEFINE(M_MDSECT, "md_sectors", "Memory Disk Sectors"); static int md_debug; SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, "Enable md(4) debug messages"); static int md_malloc_wait; SYSCTL_INT(_vm, OID_AUTO, md_malloc_wait, CTLFLAG_RW, &md_malloc_wait, 0, "Allow malloc to wait for memory allocations"); #if defined(MD_ROOT) && !defined(MD_ROOT_FSTYPE) #define MD_ROOT_FSTYPE "ufs" #endif #if defined(MD_ROOT) /* * Preloaded image gets put here. */ #if defined(MD_ROOT_SIZE) /* * We put the mfs_root symbol into the oldmfs section of the kernel object file. * Applications that patch the object with the image can determine * the size looking at the oldmfs section size within the kernel. */ u_char mfs_root[MD_ROOT_SIZE*1024] __attribute__ ((section ("oldmfs"))); const int mfs_root_size = sizeof(mfs_root); #elif defined(MD_ROOT_MEM) /* MD region already mapped in the memory */ u_char *mfs_root; int mfs_root_size; #else extern volatile u_char __weak_symbol mfs_root; extern volatile u_char __weak_symbol mfs_root_end; __GLOBL(mfs_root); __GLOBL(mfs_root_end); #define mfs_root_size ((uintptr_t)(&mfs_root_end - &mfs_root)) #endif #endif static g_init_t g_md_init; static g_fini_t g_md_fini; static g_start_t g_md_start; static g_access_t g_md_access; static void g_md_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp); static struct cdev *status_dev = NULL; static struct sx md_sx; static struct unrhdr *md_uh; static d_ioctl_t mdctlioctl; static struct cdevsw mdctl_cdevsw = { .d_version = D_VERSION, .d_ioctl = mdctlioctl, .d_name = MD_NAME, }; struct g_class g_md_class = { .name = "MD", .version = G_VERSION, .init = g_md_init, .fini = g_md_fini, .start = g_md_start, .access = g_md_access, .dumpconf = g_md_dumpconf, }; DECLARE_GEOM_CLASS(g_md_class, g_md); static LIST_HEAD(, md_s) md_softc_list = LIST_HEAD_INITIALIZER(md_softc_list); #define NINDIR (PAGE_SIZE / sizeof(uintptr_t)) #define NMASK (NINDIR-1) static int nshift; static int md_vnode_pbuf_freecnt; struct indir { uintptr_t *array; u_int total; u_int used; u_int shift; }; struct md_s { int unit; LIST_ENTRY(md_s) list; struct bio_queue_head bio_queue; struct mtx queue_mtx; struct mtx stat_mtx; struct cdev *dev; enum md_types type; off_t mediasize; unsigned sectorsize; unsigned opencount; unsigned fwheads; unsigned fwsectors; char ident[32]; unsigned flags; char name[20]; struct proc *procp; struct g_geom *gp; struct g_provider *pp; int (*start)(struct md_s *sc, struct bio *bp); struct devstat *devstat; /* MD_MALLOC related fields */ struct indir *indir; uma_zone_t uma; /* MD_PRELOAD related fields */ u_char *pl_ptr; size_t pl_len; /* MD_VNODE related fields */ struct vnode *vnode; char file[PATH_MAX]; char label[PATH_MAX]; struct ucred *cred; /* MD_SWAP related fields */ vm_object_t object; }; static struct indir * new_indir(u_int shift) { struct indir *ip; ip = malloc(sizeof *ip, M_MD, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (ip == NULL) return (NULL); ip->array = malloc(sizeof(uintptr_t) * NINDIR, M_MDSECT, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (ip->array == NULL) { free(ip, M_MD); return (NULL); } ip->total = NINDIR; ip->shift = shift; return (ip); } static void del_indir(struct indir *ip) { free(ip->array, M_MDSECT); free(ip, M_MD); } static void destroy_indir(struct md_s *sc, struct indir *ip) { int i; for (i = 0; i < NINDIR; i++) { if (!ip->array[i]) continue; if (ip->shift) destroy_indir(sc, (struct indir*)(ip->array[i])); else if (ip->array[i] > 255) uma_zfree(sc->uma, (void *)(ip->array[i])); } del_indir(ip); } /* * This function does the math and allocates the top level "indir" structure * for a device of "size" sectors. */ static struct indir * dimension(off_t size) { off_t rcnt; struct indir *ip; int layer; rcnt = size; layer = 0; while (rcnt > NINDIR) { rcnt /= NINDIR; layer++; } /* * XXX: the top layer is probably not fully populated, so we allocate * too much space for ip->array in here. */ ip = malloc(sizeof *ip, M_MD, M_WAITOK | M_ZERO); ip->array = malloc(sizeof(uintptr_t) * NINDIR, M_MDSECT, M_WAITOK | M_ZERO); ip->total = NINDIR; ip->shift = layer * nshift; return (ip); } /* * Read a given sector */ static uintptr_t s_read(struct indir *ip, off_t offset) { struct indir *cip; int idx; uintptr_t up; if (md_debug > 1) printf("s_read(%jd)\n", (intmax_t)offset); up = 0; for (cip = ip; cip != NULL;) { if (cip->shift) { idx = (offset >> cip->shift) & NMASK; up = cip->array[idx]; cip = (struct indir *)up; continue; } idx = offset & NMASK; return (cip->array[idx]); } return (0); } /* * Write a given sector, prune the tree if the value is 0 */ static int s_write(struct indir *ip, off_t offset, uintptr_t ptr) { struct indir *cip, *lip[10]; int idx, li; uintptr_t up; if (md_debug > 1) printf("s_write(%jd, %p)\n", (intmax_t)offset, (void *)ptr); up = 0; li = 0; cip = ip; for (;;) { lip[li++] = cip; if (cip->shift) { idx = (offset >> cip->shift) & NMASK; up = cip->array[idx]; if (up != 0) { cip = (struct indir *)up; continue; } /* Allocate branch */ cip->array[idx] = (uintptr_t)new_indir(cip->shift - nshift); if (cip->array[idx] == 0) return (ENOSPC); cip->used++; up = cip->array[idx]; cip = (struct indir *)up; continue; } /* leafnode */ idx = offset & NMASK; up = cip->array[idx]; if (up != 0) cip->used--; cip->array[idx] = ptr; if (ptr != 0) cip->used++; break; } if (cip->used != 0 || li == 1) return (0); li--; while (cip->used == 0 && cip != ip) { li--; idx = (offset >> lip[li]->shift) & NMASK; up = lip[li]->array[idx]; KASSERT(up == (uintptr_t)cip, ("md screwed up")); del_indir(cip); lip[li]->array[idx] = 0; lip[li]->used--; cip = lip[li]; } return (0); } static int g_md_access(struct g_provider *pp, int r, int w, int e) { struct md_s *sc; sc = pp->geom->softc; if (sc == NULL) { if (r <= 0 && w <= 0 && e <= 0) return (0); return (ENXIO); } r += pp->acr; w += pp->acw; e += pp->ace; if ((sc->flags & MD_READONLY) != 0 && w > 0) return (EROFS); if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) { sc->opencount = 1; } else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) { sc->opencount = 0; } return (0); } static void g_md_start(struct bio *bp) { struct md_s *sc; sc = bp->bio_to->geom->softc; if ((bp->bio_cmd == BIO_READ) || (bp->bio_cmd == BIO_WRITE)) { mtx_lock(&sc->stat_mtx); devstat_start_transaction_bio(sc->devstat, bp); mtx_unlock(&sc->stat_mtx); } mtx_lock(&sc->queue_mtx); bioq_disksort(&sc->bio_queue, bp); mtx_unlock(&sc->queue_mtx); wakeup(sc); } #define MD_MALLOC_MOVE_ZERO 1 #define MD_MALLOC_MOVE_FILL 2 #define MD_MALLOC_MOVE_READ 3 #define MD_MALLOC_MOVE_WRITE 4 #define MD_MALLOC_MOVE_CMP 5 static int md_malloc_move_ma(vm_page_t **mp, int *ma_offs, unsigned sectorsize, void *ptr, u_char fill, int op) { struct sf_buf *sf; vm_page_t m, *mp1; char *p, first; off_t *uc; unsigned n; int error, i, ma_offs1, sz, first_read; m = NULL; error = 0; sf = NULL; /* if (op == MD_MALLOC_MOVE_CMP) { gcc */ first = 0; first_read = 0; uc = ptr; mp1 = *mp; ma_offs1 = *ma_offs; /* } */ sched_pin(); for (n = sectorsize; n != 0; n -= sz) { sz = imin(PAGE_SIZE - *ma_offs, n); if (m != **mp) { if (sf != NULL) sf_buf_free(sf); m = **mp; sf = sf_buf_alloc(m, SFB_CPUPRIVATE | (md_malloc_wait ? 0 : SFB_NOWAIT)); if (sf == NULL) { error = ENOMEM; break; } } p = (char *)sf_buf_kva(sf) + *ma_offs; switch (op) { case MD_MALLOC_MOVE_ZERO: bzero(p, sz); break; case MD_MALLOC_MOVE_FILL: memset(p, fill, sz); break; case MD_MALLOC_MOVE_READ: bcopy(ptr, p, sz); cpu_flush_dcache(p, sz); break; case MD_MALLOC_MOVE_WRITE: bcopy(p, ptr, sz); break; case MD_MALLOC_MOVE_CMP: for (i = 0; i < sz; i++, p++) { if (!first_read) { *uc = (u_char)*p; first = *p; first_read = 1; } else if (*p != first) { error = EDOOFUS; break; } } break; default: KASSERT(0, ("md_malloc_move_ma unknown op %d\n", op)); break; } if (error != 0) break; *ma_offs += sz; *ma_offs %= PAGE_SIZE; if (*ma_offs == 0) (*mp)++; ptr = (char *)ptr + sz; } if (sf != NULL) sf_buf_free(sf); sched_unpin(); if (op == MD_MALLOC_MOVE_CMP && error != 0) { *mp = mp1; *ma_offs = ma_offs1; } return (error); } static int md_malloc_move_vlist(bus_dma_segment_t **pvlist, int *pma_offs, unsigned len, void *ptr, u_char fill, int op) { bus_dma_segment_t *vlist; uint8_t *p, *end, first; off_t *uc; int ma_offs, seg_len; vlist = *pvlist; ma_offs = *pma_offs; uc = ptr; for (; len != 0; len -= seg_len) { seg_len = imin(vlist->ds_len - ma_offs, len); p = (uint8_t *)(uintptr_t)vlist->ds_addr + ma_offs; switch (op) { case MD_MALLOC_MOVE_ZERO: bzero(p, seg_len); break; case MD_MALLOC_MOVE_FILL: memset(p, fill, seg_len); break; case MD_MALLOC_MOVE_READ: bcopy(ptr, p, seg_len); cpu_flush_dcache(p, seg_len); break; case MD_MALLOC_MOVE_WRITE: bcopy(p, ptr, seg_len); break; case MD_MALLOC_MOVE_CMP: end = p + seg_len; first = *uc = *p; /* Confirm all following bytes match the first */ while (++p < end) { if (*p != first) return (EDOOFUS); } break; default: KASSERT(0, ("md_malloc_move_vlist unknown op %d\n", op)); break; } ma_offs += seg_len; if (ma_offs == vlist->ds_len) { ma_offs = 0; vlist++; } ptr = (uint8_t *)ptr + seg_len; } *pvlist = vlist; *pma_offs = ma_offs; return (0); } static int mdstart_malloc(struct md_s *sc, struct bio *bp) { u_char *dst; vm_page_t *m; bus_dma_segment_t *vlist; int i, error, error1, ma_offs, notmapped; off_t secno, nsec, uc; uintptr_t sp, osp; switch (bp->bio_cmd) { case BIO_READ: case BIO_WRITE: case BIO_DELETE: break; default: return (EOPNOTSUPP); } notmapped = (bp->bio_flags & BIO_UNMAPPED) != 0; vlist = (bp->bio_flags & BIO_VLIST) != 0 ? (bus_dma_segment_t *)bp->bio_data : NULL; if (notmapped) { m = bp->bio_ma; ma_offs = bp->bio_ma_offset; dst = NULL; KASSERT(vlist == NULL, ("vlists cannot be unmapped")); } else if (vlist != NULL) { ma_offs = bp->bio_ma_offset; dst = NULL; } else { dst = bp->bio_data; } nsec = bp->bio_length / sc->sectorsize; secno = bp->bio_offset / sc->sectorsize; error = 0; while (nsec--) { osp = s_read(sc->indir, secno); if (bp->bio_cmd == BIO_DELETE) { if (osp != 0) error = s_write(sc->indir, secno, 0); } else if (bp->bio_cmd == BIO_READ) { if (osp == 0) { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, NULL, 0, MD_MALLOC_MOVE_ZERO); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, NULL, 0, MD_MALLOC_MOVE_ZERO); } else bzero(dst, sc->sectorsize); } else if (osp <= 255) { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, NULL, osp, MD_MALLOC_MOVE_FILL); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, NULL, osp, MD_MALLOC_MOVE_FILL); } else memset(dst, osp, sc->sectorsize); } else { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_READ); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_READ); } else { bcopy((void *)osp, dst, sc->sectorsize); cpu_flush_dcache(dst, sc->sectorsize); } } osp = 0; } else if (bp->bio_cmd == BIO_WRITE) { if (sc->flags & MD_COMPRESS) { if (notmapped) { error1 = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, &uc, 0, MD_MALLOC_MOVE_CMP); i = error1 == 0 ? sc->sectorsize : 0; } else if (vlist != NULL) { error1 = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, &uc, 0, MD_MALLOC_MOVE_CMP); i = error1 == 0 ? sc->sectorsize : 0; } else { uc = dst[0]; for (i = 1; i < sc->sectorsize; i++) { if (dst[i] != uc) break; } } } else { i = 0; uc = 0; } if (i == sc->sectorsize) { if (osp != uc) error = s_write(sc->indir, secno, uc); } else { if (osp <= 255) { sp = (uintptr_t)uma_zalloc(sc->uma, md_malloc_wait ? M_WAITOK : M_NOWAIT); if (sp == 0) { error = ENOSPC; break; } if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)sp, 0, MD_MALLOC_MOVE_WRITE); } else if (vlist != NULL) { error = md_malloc_move_vlist( &vlist, &ma_offs, sc->sectorsize, (void *)sp, 0, MD_MALLOC_MOVE_WRITE); } else { bcopy(dst, (void *)sp, sc->sectorsize); } error = s_write(sc->indir, secno, sp); } else { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_WRITE); } else if (vlist != NULL) { error = md_malloc_move_vlist( &vlist, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_WRITE); } else { bcopy(dst, (void *)osp, sc->sectorsize); } osp = 0; } } } else { error = EOPNOTSUPP; } if (osp > 255) uma_zfree(sc->uma, (void*)osp); if (error != 0) break; secno++; if (!notmapped && vlist == NULL) dst += sc->sectorsize; } bp->bio_resid = 0; return (error); } static void mdcopyto_vlist(void *src, bus_dma_segment_t *vlist, off_t offset, off_t len) { off_t seg_len; while (offset >= vlist->ds_len) { offset -= vlist->ds_len; vlist++; } while (len != 0) { seg_len = omin(len, vlist->ds_len - offset); bcopy(src, (void *)(uintptr_t)(vlist->ds_addr + offset), seg_len); offset = 0; src = (uint8_t *)src + seg_len; len -= seg_len; vlist++; } } static void mdcopyfrom_vlist(bus_dma_segment_t *vlist, off_t offset, void *dst, off_t len) { off_t seg_len; while (offset >= vlist->ds_len) { offset -= vlist->ds_len; vlist++; } while (len != 0) { seg_len = omin(len, vlist->ds_len - offset); bcopy((void *)(uintptr_t)(vlist->ds_addr + offset), dst, seg_len); offset = 0; dst = (uint8_t *)dst + seg_len; len -= seg_len; vlist++; } } static int mdstart_preload(struct md_s *sc, struct bio *bp) { uint8_t *p; p = sc->pl_ptr + bp->bio_offset; switch (bp->bio_cmd) { case BIO_READ: if ((bp->bio_flags & BIO_VLIST) != 0) { mdcopyto_vlist(p, (bus_dma_segment_t *)bp->bio_data, bp->bio_ma_offset, bp->bio_length); } else { bcopy(p, bp->bio_data, bp->bio_length); } cpu_flush_dcache(bp->bio_data, bp->bio_length); break; case BIO_WRITE: if ((bp->bio_flags & BIO_VLIST) != 0) { mdcopyfrom_vlist((bus_dma_segment_t *)bp->bio_data, bp->bio_ma_offset, p, bp->bio_length); } else { bcopy(bp->bio_data, p, bp->bio_length); } break; } bp->bio_resid = 0; return (0); } static int mdstart_vnode(struct md_s *sc, struct bio *bp) { int error; struct uio auio; struct iovec aiov; struct iovec *piov; struct mount *mp; struct vnode *vp; struct buf *pb; bus_dma_segment_t *vlist; struct thread *td; off_t iolen, len, zerosize; int ma_offs, npages; switch (bp->bio_cmd) { case BIO_READ: auio.uio_rw = UIO_READ; break; case BIO_WRITE: case BIO_DELETE: auio.uio_rw = UIO_WRITE; break; case BIO_FLUSH: break; default: return (EOPNOTSUPP); } td = curthread; vp = sc->vnode; pb = NULL; piov = NULL; ma_offs = bp->bio_ma_offset; len = bp->bio_length; /* * VNODE I/O * * If an error occurs, we set BIO_ERROR but we do not set * B_INVAL because (for a write anyway), the buffer is * still valid. */ if (bp->bio_cmd == BIO_FLUSH) { (void) vn_start_write(vp, &mp, V_WAIT); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_FSYNC(vp, MNT_WAIT, td); VOP_UNLOCK(vp, 0); vn_finished_write(mp); return (error); } auio.uio_offset = (vm_ooffset_t)bp->bio_offset; auio.uio_resid = bp->bio_length; auio.uio_segflg = UIO_SYSSPACE; auio.uio_td = td; if (bp->bio_cmd == BIO_DELETE) { /* * Emulate BIO_DELETE by writing zeros. */ zerosize = ZERO_REGION_SIZE - (ZERO_REGION_SIZE % sc->sectorsize); auio.uio_iovcnt = howmany(bp->bio_length, zerosize); piov = malloc(sizeof(*piov) * auio.uio_iovcnt, M_MD, M_WAITOK); auio.uio_iov = piov; while (len > 0) { piov->iov_base = __DECONST(void *, zero_region); piov->iov_len = len; if (len > zerosize) piov->iov_len = zerosize; len -= piov->iov_len; piov++; } piov = auio.uio_iov; } else if ((bp->bio_flags & BIO_VLIST) != 0) { piov = malloc(sizeof(*piov) * bp->bio_ma_n, M_MD, M_WAITOK); auio.uio_iov = piov; vlist = (bus_dma_segment_t *)bp->bio_data; while (len > 0) { piov->iov_base = (void *)(uintptr_t)(vlist->ds_addr + ma_offs); piov->iov_len = vlist->ds_len - ma_offs; if (piov->iov_len > len) piov->iov_len = len; len -= piov->iov_len; ma_offs = 0; vlist++; piov++; } auio.uio_iovcnt = piov - auio.uio_iov; piov = auio.uio_iov; } else if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pb = getpbuf(&md_vnode_pbuf_freecnt); bp->bio_resid = len; unmapped_step: npages = atop(min(MAXPHYS, round_page(len + (ma_offs & PAGE_MASK)))); iolen = min(ptoa(npages) - (ma_offs & PAGE_MASK), len); KASSERT(iolen > 0, ("zero iolen")); pmap_qenter((vm_offset_t)pb->b_data, &bp->bio_ma[atop(ma_offs)], npages); aiov.iov_base = (void *)((vm_offset_t)pb->b_data + (ma_offs & PAGE_MASK)); aiov.iov_len = iolen; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = iolen; } else { aiov.iov_base = bp->bio_data; aiov.iov_len = bp->bio_length; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; } /* * When reading set IO_DIRECT to try to avoid double-caching * the data. When writing IO_DIRECT is not optimal. */ if (auio.uio_rw == UIO_READ) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_READ(vp, &auio, IO_DIRECT, sc->cred); VOP_UNLOCK(vp, 0); } else { (void) vn_start_write(vp, &mp, V_WAIT); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_WRITE(vp, &auio, sc->flags & MD_ASYNC ? 0 : IO_SYNC, sc->cred); VOP_UNLOCK(vp, 0); vn_finished_write(mp); if (error == 0) sc->flags &= ~MD_VERIFY; } if (pb != NULL) { pmap_qremove((vm_offset_t)pb->b_data, npages); if (error == 0) { len -= iolen; bp->bio_resid -= iolen; ma_offs += iolen; if (len > 0) goto unmapped_step; } relpbuf(pb, &md_vnode_pbuf_freecnt); } free(piov, M_MD); if (pb == NULL) bp->bio_resid = auio.uio_resid; return (error); } static void md_swap_page_free(vm_page_t m) { vm_page_xunbusy(m); vm_page_lock(m); vm_page_free(m); vm_page_unlock(m); } static int mdstart_swap(struct md_s *sc, struct bio *bp) { vm_page_t m; u_char *p; vm_pindex_t i, lastp; bus_dma_segment_t *vlist; int rv, ma_offs, offs, len, lastend; switch (bp->bio_cmd) { case BIO_READ: case BIO_WRITE: case BIO_DELETE: break; default: return (EOPNOTSUPP); } p = bp->bio_data; ma_offs = (bp->bio_flags & (BIO_UNMAPPED|BIO_VLIST)) != 0 ? bp->bio_ma_offset : 0; vlist = (bp->bio_flags & BIO_VLIST) != 0 ? (bus_dma_segment_t *)bp->bio_data : NULL; /* * offs is the offset at which to start operating on the * next (ie, first) page. lastp is the last page on * which we're going to operate. lastend is the ending * position within that last page (ie, PAGE_SIZE if * we're operating on complete aligned pages). */ offs = bp->bio_offset % PAGE_SIZE; lastp = (bp->bio_offset + bp->bio_length - 1) / PAGE_SIZE; lastend = (bp->bio_offset + bp->bio_length - 1) % PAGE_SIZE + 1; rv = VM_PAGER_OK; VM_OBJECT_WLOCK(sc->object); vm_object_pip_add(sc->object, 1); for (i = bp->bio_offset / PAGE_SIZE; i <= lastp; i++) { len = ((i == lastp) ? lastend : PAGE_SIZE) - offs; m = vm_page_grab(sc->object, i, VM_ALLOC_SYSTEM); if (bp->bio_cmd == BIO_READ) { if (m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) { /* * Pager does not have the page. Zero * the allocated page, and mark it as * valid. Do not set dirty, the page * can be recreated if thrown out. */ pmap_zero_page(m); m->valid = VM_PAGE_BITS_ALL; } if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pmap_copy_pages(&m, offs, bp->bio_ma, ma_offs, len); } else if ((bp->bio_flags & BIO_VLIST) != 0) { physcopyout_vlist(VM_PAGE_TO_PHYS(m) + offs, vlist, ma_offs, len); cpu_flush_dcache(p, len); } else { physcopyout(VM_PAGE_TO_PHYS(m) + offs, p, len); cpu_flush_dcache(p, len); } } else if (bp->bio_cmd == BIO_WRITE) { if (len == PAGE_SIZE || m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) pmap_zero_page(m); if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pmap_copy_pages(bp->bio_ma, ma_offs, &m, offs, len); } else if ((bp->bio_flags & BIO_VLIST) != 0) { physcopyin_vlist(vlist, ma_offs, VM_PAGE_TO_PHYS(m) + offs, len); } else { physcopyin(p, VM_PAGE_TO_PHYS(m) + offs, len); } m->valid = VM_PAGE_BITS_ALL; if (m->dirty != VM_PAGE_BITS_ALL) { vm_page_dirty(m); vm_pager_page_unswapped(m); } } else if (bp->bio_cmd == BIO_DELETE) { if (len == PAGE_SIZE || m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) { md_swap_page_free(m); m = NULL; } else { /* Page is valid. */ if (len != PAGE_SIZE) { pmap_zero_page_area(m, offs, len); if (m->dirty != VM_PAGE_BITS_ALL) { vm_page_dirty(m); vm_pager_page_unswapped(m); } } else { vm_pager_page_unswapped(m); md_swap_page_free(m); m = NULL; } } } if (m != NULL) { vm_page_xunbusy(m); vm_page_lock(m); if (vm_page_active(m)) vm_page_reference(m); else vm_page_activate(m); vm_page_unlock(m); } /* Actions on further pages start at offset 0 */ p += PAGE_SIZE - offs; offs = 0; ma_offs += len; } vm_object_pip_wakeup(sc->object); VM_OBJECT_WUNLOCK(sc->object); return (rv != VM_PAGER_ERROR ? 0 : ENOSPC); } static int mdstart_null(struct md_s *sc, struct bio *bp) { switch (bp->bio_cmd) { case BIO_READ: bzero(bp->bio_data, bp->bio_length); cpu_flush_dcache(bp->bio_data, bp->bio_length); break; case BIO_WRITE: break; } bp->bio_resid = 0; return (0); } static void md_kthread(void *arg) { struct md_s *sc; struct bio *bp; int error; sc = arg; thread_lock(curthread); sched_prio(curthread, PRIBIO); thread_unlock(curthread); if (sc->type == MD_VNODE) curthread->td_pflags |= TDP_NORUNNINGBUF; for (;;) { mtx_lock(&sc->queue_mtx); if (sc->flags & MD_SHUTDOWN) { sc->flags |= MD_EXITING; mtx_unlock(&sc->queue_mtx); kproc_exit(0); } bp = bioq_takefirst(&sc->bio_queue); if (!bp) { msleep(sc, &sc->queue_mtx, PRIBIO | PDROP, "mdwait", 0); continue; } mtx_unlock(&sc->queue_mtx); if (bp->bio_cmd == BIO_GETATTR) { int isv = ((sc->flags & MD_VERIFY) != 0); if ((sc->fwsectors && sc->fwheads && (g_handleattr_int(bp, "GEOM::fwsectors", sc->fwsectors) || g_handleattr_int(bp, "GEOM::fwheads", sc->fwheads))) || g_handleattr_int(bp, "GEOM::candelete", 1)) error = -1; else if (sc->ident[0] != '\0' && g_handleattr_str(bp, "GEOM::ident", sc->ident)) error = -1; else if (g_handleattr_int(bp, "MNT::verified", isv)) error = -1; else error = EOPNOTSUPP; } else { error = sc->start(sc, bp); } if (error != -1) { bp->bio_completed = bp->bio_length; - if ((bp->bio_cmd == BIO_READ) || (bp->bio_cmd == BIO_WRITE)) - devstat_end_transaction_bio(sc->devstat, bp); g_io_deliver(bp, error); } + if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) + devstat_end_transaction_bio(sc->devstat, bp); } } static struct md_s * mdfind(int unit) { struct md_s *sc; LIST_FOREACH(sc, &md_softc_list, list) { if (sc->unit == unit) break; } return (sc); } static struct md_s * mdnew(int unit, int *errp, enum md_types type) { struct md_s *sc; int error; *errp = 0; if (unit == -1) unit = alloc_unr(md_uh); else unit = alloc_unr_specific(md_uh, unit); if (unit == -1) { *errp = EBUSY; return (NULL); } sc = (struct md_s *)malloc(sizeof *sc, M_MD, M_WAITOK | M_ZERO); sc->type = type; bioq_init(&sc->bio_queue); mtx_init(&sc->queue_mtx, "md bio queue", NULL, MTX_DEF); mtx_init(&sc->stat_mtx, "md stat", NULL, MTX_DEF); sc->unit = unit; sprintf(sc->name, "md%d", unit); LIST_INSERT_HEAD(&md_softc_list, sc, list); error = kproc_create(md_kthread, sc, &sc->procp, 0, 0,"%s", sc->name); if (error == 0) return (sc); LIST_REMOVE(sc, list); mtx_destroy(&sc->stat_mtx); mtx_destroy(&sc->queue_mtx); free_unr(md_uh, sc->unit); free(sc, M_MD); *errp = error; return (NULL); } static void mdinit(struct md_s *sc) { struct g_geom *gp; struct g_provider *pp; g_topology_lock(); gp = g_new_geomf(&g_md_class, "md%d", sc->unit); gp->softc = sc; pp = g_new_providerf(gp, "md%d", sc->unit); pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE; pp->mediasize = sc->mediasize; pp->sectorsize = sc->sectorsize; switch (sc->type) { case MD_MALLOC: case MD_VNODE: case MD_SWAP: pp->flags |= G_PF_ACCEPT_UNMAPPED; break; case MD_PRELOAD: case MD_NULL: break; } sc->gp = gp; sc->pp = pp; g_error_provider(pp, 0); g_topology_unlock(); sc->devstat = devstat_new_entry("md", sc->unit, sc->sectorsize, DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX); } static int mdcreate_malloc(struct md_s *sc, struct md_req *mdr) { uintptr_t sp; int error; off_t u; error = 0; if (mdr->md_options & ~(MD_AUTOUNIT | MD_COMPRESS | MD_RESERVE)) return (EINVAL); if (mdr->md_sectorsize != 0 && !powerof2(mdr->md_sectorsize)) return (EINVAL); /* Compression doesn't make sense if we have reserved space */ if (mdr->md_options & MD_RESERVE) mdr->md_options &= ~MD_COMPRESS; if (mdr->md_fwsectors != 0) sc->fwsectors = mdr->md_fwsectors; if (mdr->md_fwheads != 0) sc->fwheads = mdr->md_fwheads; sc->flags = mdr->md_options & (MD_COMPRESS | MD_FORCE); sc->indir = dimension(sc->mediasize / sc->sectorsize); sc->uma = uma_zcreate(sc->name, sc->sectorsize, NULL, NULL, NULL, NULL, 0x1ff, 0); if (mdr->md_options & MD_RESERVE) { off_t nsectors; nsectors = sc->mediasize / sc->sectorsize; for (u = 0; u < nsectors; u++) { sp = (uintptr_t)uma_zalloc(sc->uma, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (sp != 0) error = s_write(sc->indir, u, sp); else error = ENOMEM; if (error != 0) break; } } return (error); } static int mdsetcred(struct md_s *sc, struct ucred *cred) { char *tmpbuf; int error = 0; /* * Set credits in our softc */ if (sc->cred) crfree(sc->cred); sc->cred = crhold(cred); /* * Horrible kludge to establish credentials for NFS XXX. */ if (sc->vnode) { struct uio auio; struct iovec aiov; tmpbuf = malloc(sc->sectorsize, M_TEMP, M_WAITOK); bzero(&auio, sizeof(auio)); aiov.iov_base = tmpbuf; aiov.iov_len = sc->sectorsize; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = aiov.iov_len; vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY); error = VOP_READ(sc->vnode, &auio, 0, sc->cred); VOP_UNLOCK(sc->vnode, 0); free(tmpbuf, M_TEMP); } return (error); } static int mdcreate_vnode(struct md_s *sc, struct md_req *mdr, struct thread *td) { struct vattr vattr; struct nameidata nd; char *fname; int error, flags; fname = mdr->md_file; if (mdr->md_file_seg == UIO_USERSPACE) { error = copyinstr(fname, sc->file, sizeof(sc->file), NULL); if (error != 0) return (error); } else if (mdr->md_file_seg == UIO_SYSSPACE) strlcpy(sc->file, fname, sizeof(sc->file)); else return (EDOOFUS); /* * If the user specified that this is a read only device, don't * set the FWRITE mask before trying to open the backing store. */ flags = FREAD | ((mdr->md_options & MD_READONLY) ? 0 : FWRITE) \ | ((mdr->md_options & MD_VERIFY) ? O_VERIFY : 0); NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, sc->file, td); error = vn_open(&nd, &flags, 0, NULL); if (error != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp->v_type != VREG) { error = EINVAL; goto bad; } error = VOP_GETATTR(nd.ni_vp, &vattr, td->td_ucred); if (error != 0) goto bad; if (VOP_ISLOCKED(nd.ni_vp) != LK_EXCLUSIVE) { vn_lock(nd.ni_vp, LK_UPGRADE | LK_RETRY); if (nd.ni_vp->v_iflag & VI_DOOMED) { /* Forced unmount. */ error = EBADF; goto bad; } } nd.ni_vp->v_vflag |= VV_MD; VOP_UNLOCK(nd.ni_vp, 0); if (mdr->md_fwsectors != 0) sc->fwsectors = mdr->md_fwsectors; if (mdr->md_fwheads != 0) sc->fwheads = mdr->md_fwheads; snprintf(sc->ident, sizeof(sc->ident), "MD-DEV%ju-INO%ju", (uintmax_t)vattr.va_fsid, (uintmax_t)vattr.va_fileid); sc->flags = mdr->md_options & (MD_FORCE | MD_ASYNC | MD_VERIFY); if (!(flags & FWRITE)) sc->flags |= MD_READONLY; sc->vnode = nd.ni_vp; error = mdsetcred(sc, td->td_ucred); if (error != 0) { sc->vnode = NULL; vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY); nd.ni_vp->v_vflag &= ~VV_MD; goto bad; } return (0); bad: VOP_UNLOCK(nd.ni_vp, 0); (void)vn_close(nd.ni_vp, flags, td->td_ucred, td); return (error); } static int mddestroy(struct md_s *sc, struct thread *td) { if (sc->gp) { sc->gp->softc = NULL; g_topology_lock(); g_wither_geom(sc->gp, ENXIO); g_topology_unlock(); sc->gp = NULL; sc->pp = NULL; } if (sc->devstat) { devstat_remove_entry(sc->devstat); sc->devstat = NULL; } mtx_lock(&sc->queue_mtx); sc->flags |= MD_SHUTDOWN; wakeup(sc); while (!(sc->flags & MD_EXITING)) msleep(sc->procp, &sc->queue_mtx, PRIBIO, "mddestroy", hz / 10); mtx_unlock(&sc->queue_mtx); mtx_destroy(&sc->stat_mtx); mtx_destroy(&sc->queue_mtx); if (sc->vnode != NULL) { vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY); sc->vnode->v_vflag &= ~VV_MD; VOP_UNLOCK(sc->vnode, 0); (void)vn_close(sc->vnode, sc->flags & MD_READONLY ? FREAD : (FREAD|FWRITE), sc->cred, td); } if (sc->cred != NULL) crfree(sc->cred); if (sc->object != NULL) vm_object_deallocate(sc->object); if (sc->indir) destroy_indir(sc, sc->indir); if (sc->uma) uma_zdestroy(sc->uma); LIST_REMOVE(sc, list); free_unr(md_uh, sc->unit); free(sc, M_MD); return (0); } static int mdresize(struct md_s *sc, struct md_req *mdr) { int error, res; vm_pindex_t oldpages, newpages; switch (sc->type) { case MD_VNODE: case MD_NULL: break; case MD_SWAP: if (mdr->md_mediasize <= 0 || (mdr->md_mediasize % PAGE_SIZE) != 0) return (EDOM); oldpages = OFF_TO_IDX(round_page(sc->mediasize)); newpages = OFF_TO_IDX(round_page(mdr->md_mediasize)); if (newpages < oldpages) { VM_OBJECT_WLOCK(sc->object); vm_object_page_remove(sc->object, newpages, 0, 0); swap_pager_freespace(sc->object, newpages, oldpages - newpages); swap_release_by_cred(IDX_TO_OFF(oldpages - newpages), sc->cred); sc->object->charge = IDX_TO_OFF(newpages); sc->object->size = newpages; VM_OBJECT_WUNLOCK(sc->object); } else if (newpages > oldpages) { res = swap_reserve_by_cred(IDX_TO_OFF(newpages - oldpages), sc->cred); if (!res) return (ENOMEM); if ((mdr->md_options & MD_RESERVE) || (sc->flags & MD_RESERVE)) { error = swap_pager_reserve(sc->object, oldpages, newpages - oldpages); if (error < 0) { swap_release_by_cred( IDX_TO_OFF(newpages - oldpages), sc->cred); return (EDOM); } } VM_OBJECT_WLOCK(sc->object); sc->object->charge = IDX_TO_OFF(newpages); sc->object->size = newpages; VM_OBJECT_WUNLOCK(sc->object); } break; default: return (EOPNOTSUPP); } sc->mediasize = mdr->md_mediasize; g_topology_lock(); g_resize_provider(sc->pp, sc->mediasize); g_topology_unlock(); return (0); } static int mdcreate_swap(struct md_s *sc, struct md_req *mdr, struct thread *td) { vm_ooffset_t npage; int error; /* * Range check. Disallow negative sizes and sizes not being * multiple of page size. */ if (sc->mediasize <= 0 || (sc->mediasize % PAGE_SIZE) != 0) return (EDOM); /* * Allocate an OBJT_SWAP object. * * Note the truncation. */ if ((mdr->md_options & MD_VERIFY) != 0) return (EINVAL); npage = mdr->md_mediasize / PAGE_SIZE; if (mdr->md_fwsectors != 0) sc->fwsectors = mdr->md_fwsectors; if (mdr->md_fwheads != 0) sc->fwheads = mdr->md_fwheads; sc->object = vm_pager_allocate(OBJT_SWAP, NULL, PAGE_SIZE * npage, VM_PROT_DEFAULT, 0, td->td_ucred); if (sc->object == NULL) return (ENOMEM); sc->flags = mdr->md_options & (MD_FORCE | MD_RESERVE); if (mdr->md_options & MD_RESERVE) { if (swap_pager_reserve(sc->object, 0, npage) < 0) { error = EDOM; goto finish; } } error = mdsetcred(sc, td->td_ucred); finish: if (error != 0) { vm_object_deallocate(sc->object); sc->object = NULL; } return (error); } static int mdcreate_null(struct md_s *sc, struct md_req *mdr, struct thread *td) { /* * Range check. Disallow negative sizes and sizes not being * multiple of page size. */ if (sc->mediasize <= 0 || (sc->mediasize % PAGE_SIZE) != 0) return (EDOM); return (0); } static int kern_mdattach_locked(struct thread *td, struct md_req *mdr) { struct md_s *sc; unsigned sectsize; int error, i; sx_assert(&md_sx, SA_XLOCKED); switch (mdr->md_type) { case MD_MALLOC: case MD_PRELOAD: case MD_VNODE: case MD_SWAP: case MD_NULL: break; default: return (EINVAL); } if (mdr->md_sectorsize == 0) sectsize = DEV_BSIZE; else sectsize = mdr->md_sectorsize; if (sectsize > MAXPHYS || mdr->md_mediasize < sectsize) return (EINVAL); if (mdr->md_options & MD_AUTOUNIT) sc = mdnew(-1, &error, mdr->md_type); else { if (mdr->md_unit > INT_MAX) return (EINVAL); sc = mdnew(mdr->md_unit, &error, mdr->md_type); } if (sc == NULL) return (error); if (mdr->md_label != NULL) error = copyinstr(mdr->md_label, sc->label, sizeof(sc->label), NULL); if (error != 0) goto err_after_new; if (mdr->md_options & MD_AUTOUNIT) mdr->md_unit = sc->unit; sc->mediasize = mdr->md_mediasize; sc->sectorsize = sectsize; error = EDOOFUS; switch (sc->type) { case MD_MALLOC: sc->start = mdstart_malloc; error = mdcreate_malloc(sc, mdr); break; case MD_PRELOAD: /* * We disallow attaching preloaded memory disks via * ioctl. Preloaded memory disks are automatically * attached in g_md_init(). */ error = EOPNOTSUPP; break; case MD_VNODE: sc->start = mdstart_vnode; error = mdcreate_vnode(sc, mdr, td); break; case MD_SWAP: sc->start = mdstart_swap; error = mdcreate_swap(sc, mdr, td); break; case MD_NULL: sc->start = mdstart_null; error = mdcreate_null(sc, mdr, td); break; } err_after_new: if (error != 0) { mddestroy(sc, td); return (error); } /* Prune off any residual fractional sector */ i = sc->mediasize % sc->sectorsize; sc->mediasize -= i; mdinit(sc); return (0); } static int kern_mdattach(struct thread *td, struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mdattach_locked(td, mdr); sx_xunlock(&md_sx); return (error); } static int kern_mddetach_locked(struct thread *td, struct md_req *mdr) { struct md_s *sc; sx_assert(&md_sx, SA_XLOCKED); if (mdr->md_mediasize != 0 || (mdr->md_options & ~MD_FORCE) != 0) return (EINVAL); sc = mdfind(mdr->md_unit); if (sc == NULL) return (ENOENT); if (sc->opencount != 0 && !(sc->flags & MD_FORCE) && !(mdr->md_options & MD_FORCE)) return (EBUSY); return (mddestroy(sc, td)); } static int kern_mddetach(struct thread *td, struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mddetach_locked(td, mdr); sx_xunlock(&md_sx); return (error); } static int kern_mdresize_locked(struct md_req *mdr) { struct md_s *sc; sx_assert(&md_sx, SA_XLOCKED); if ((mdr->md_options & ~(MD_FORCE | MD_RESERVE)) != 0) return (EINVAL); sc = mdfind(mdr->md_unit); if (sc == NULL) return (ENOENT); if (mdr->md_mediasize < sc->sectorsize) return (EINVAL); if (mdr->md_mediasize < sc->mediasize && !(sc->flags & MD_FORCE) && !(mdr->md_options & MD_FORCE)) return (EBUSY); return (mdresize(sc, mdr)); } static int kern_mdresize(struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mdresize_locked(mdr); sx_xunlock(&md_sx); return (error); } static int kern_mdquery_locked(struct md_req *mdr) { struct md_s *sc; int error; sx_assert(&md_sx, SA_XLOCKED); sc = mdfind(mdr->md_unit); if (sc == NULL) return (ENOENT); mdr->md_type = sc->type; mdr->md_options = sc->flags; mdr->md_mediasize = sc->mediasize; mdr->md_sectorsize = sc->sectorsize; error = 0; if (mdr->md_label != NULL) { error = copyout(sc->label, mdr->md_label, strlen(sc->label) + 1); if (error != 0) return (error); } if (sc->type == MD_VNODE || (sc->type == MD_PRELOAD && mdr->md_file != NULL)) error = copyout(sc->file, mdr->md_file, strlen(sc->file) + 1); return (error); } static int kern_mdquery(struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mdquery_locked(mdr); sx_xunlock(&md_sx); return (error); } static int kern_mdlist_locked(struct md_req *mdr) { struct md_s *sc; int i; sx_assert(&md_sx, SA_XLOCKED); /* * Write the number of md devices to mdr->md_units[0]. * Write the unit number of the first (mdr->md_units_nitems - 2) * units to mdr->md_units[1::(mdr->md_units - 2)] and terminate the * list with -1. * * XXX: There is currently no mechanism to retrieve unit * numbers for more than (MDNPAD - 2) units. * * XXX: Due to the use of LIST_INSERT_HEAD in mdnew(), the * list of visible unit numbers not stable. */ i = 1; LIST_FOREACH(sc, &md_softc_list, list) { if (i < mdr->md_units_nitems - 1) mdr->md_units[i] = sc->unit; i++; } mdr->md_units[MIN(i, mdr->md_units_nitems - 1)] = -1; mdr->md_units[0] = i - 1; return (0); } static int kern_mdlist(struct md_req *mdr) { int error; sx_xlock(&md_sx); error = kern_mdlist_locked(mdr); sx_xunlock(&md_sx); return (error); } /* Copy members that are not userspace pointers. */ #define MD_IOCTL2REQ(mdio, mdr) do { \ (mdr)->md_unit = (mdio)->md_unit; \ (mdr)->md_type = (mdio)->md_type; \ (mdr)->md_mediasize = (mdio)->md_mediasize; \ (mdr)->md_sectorsize = (mdio)->md_sectorsize; \ (mdr)->md_options = (mdio)->md_options; \ (mdr)->md_fwheads = (mdio)->md_fwheads; \ (mdr)->md_fwsectors = (mdio)->md_fwsectors; \ (mdr)->md_units = &(mdio)->md_pad[0]; \ (mdr)->md_units_nitems = nitems((mdio)->md_pad); \ } while(0) /* Copy members that might have been updated */ #define MD_REQ2IOCTL(mdr, mdio) do { \ (mdio)->md_unit = (mdr)->md_unit; \ (mdio)->md_type = (mdr)->md_type; \ (mdio)->md_mediasize = (mdr)->md_mediasize; \ (mdio)->md_sectorsize = (mdr)->md_sectorsize; \ (mdio)->md_options = (mdr)->md_options; \ (mdio)->md_fwheads = (mdr)->md_fwheads; \ (mdio)->md_fwsectors = (mdr)->md_fwsectors; \ } while(0) static int mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct md_req mdr; int error; if (md_debug) printf("mdctlioctl(%s %lx %p %x %p)\n", devtoname(dev), cmd, addr, flags, td); bzero(&mdr, sizeof(mdr)); switch (cmd) { case MDIOCATTACH: case MDIOCDETACH: case MDIOCRESIZE: case MDIOCQUERY: case MDIOCLIST: { struct md_ioctl *mdio = (struct md_ioctl *)addr; if (mdio->md_version != MDIOVERSION) return (EINVAL); MD_IOCTL2REQ(mdio, &mdr); mdr.md_file = mdio->md_file; mdr.md_file_seg = UIO_USERSPACE; /* If the file is adjacent to the md_ioctl it's in kernel. */ if ((void *)mdio->md_file == (void *)(mdio + 1)) mdr.md_file_seg = UIO_SYSSPACE; mdr.md_label = mdio->md_label; break; } #ifdef COMPAT_FREEBSD32 case MDIOCATTACH_32: case MDIOCDETACH_32: case MDIOCRESIZE_32: case MDIOCQUERY_32: case MDIOCLIST_32: { struct md_ioctl32 *mdio = (struct md_ioctl32 *)addr; if (mdio->md_version != MDIOVERSION) return (EINVAL); MD_IOCTL2REQ(mdio, &mdr); mdr.md_file = (void *)(uintptr_t)mdio->md_file; mdr.md_file_seg = UIO_USERSPACE; mdr.md_label = (void *)(uintptr_t)mdio->md_label; break; } #endif default: /* Fall through to handler switch. */ break; } error = 0; switch (cmd) { case MDIOCATTACH: #ifdef COMPAT_FREEBSD32 case MDIOCATTACH_32: #endif error = kern_mdattach(td, &mdr); break; case MDIOCDETACH: #ifdef COMPAT_FREEBSD32 case MDIOCDETACH_32: #endif error = kern_mddetach(td, &mdr); break; case MDIOCRESIZE: #ifdef COMPAT_FREEBSD32 case MDIOCRESIZE_32: #endif error = kern_mdresize(&mdr); break; case MDIOCQUERY: #ifdef COMPAT_FREEBSD32 case MDIOCQUERY_32: #endif error = kern_mdquery(&mdr); break; case MDIOCLIST: #ifdef COMPAT_FREEBSD32 case MDIOCLIST_32: #endif error = kern_mdlist(&mdr); break; default: error = ENOIOCTL; } switch (cmd) { case MDIOCATTACH: case MDIOCQUERY: { struct md_ioctl *mdio = (struct md_ioctl *)addr; MD_REQ2IOCTL(&mdr, mdio); break; } #ifdef COMPAT_FREEBSD32 case MDIOCATTACH_32: case MDIOCQUERY_32: { struct md_ioctl32 *mdio = (struct md_ioctl32 *)addr; MD_REQ2IOCTL(&mdr, mdio); break; } #endif default: /* Other commands to not alter mdr. */ break; } return (error); } static void md_preloaded(u_char *image, size_t length, const char *name) { struct md_s *sc; int error; sc = mdnew(-1, &error, MD_PRELOAD); if (sc == NULL) return; sc->mediasize = length; sc->sectorsize = DEV_BSIZE; sc->pl_ptr = image; sc->pl_len = length; sc->start = mdstart_preload; if (name != NULL) strlcpy(sc->file, name, sizeof(sc->file)); #ifdef MD_ROOT if (sc->unit == 0) { #ifndef ROOTDEVNAME rootdevnames[0] = MD_ROOT_FSTYPE ":/dev/md0"; #endif #ifdef MD_ROOT_READONLY sc->flags |= MD_READONLY; #endif } #endif mdinit(sc); if (name != NULL) { printf("%s%d: Preloaded image <%s> %zd bytes at %p\n", MD_NAME, sc->unit, name, length, image); } else { printf("%s%d: Embedded image %zd bytes at %p\n", MD_NAME, sc->unit, length, image); } } static void g_md_init(struct g_class *mp __unused) { caddr_t mod; u_char *ptr, *name, *type; unsigned len; int i; /* figure out log2(NINDIR) */ for (i = NINDIR, nshift = -1; i; nshift++) i >>= 1; mod = NULL; sx_init(&md_sx, "MD config lock"); g_topology_unlock(); md_uh = new_unrhdr(0, INT_MAX, NULL); #ifdef MD_ROOT if (mfs_root_size != 0) { sx_xlock(&md_sx); #ifdef MD_ROOT_MEM md_preloaded(mfs_root, mfs_root_size, NULL); #else md_preloaded(__DEVOLATILE(u_char *, &mfs_root), mfs_root_size, NULL); #endif sx_xunlock(&md_sx); } #endif /* XXX: are preload_* static or do they need Giant ? */ while ((mod = preload_search_next_name(mod)) != NULL) { name = (char *)preload_search_info(mod, MODINFO_NAME); if (name == NULL) continue; type = (char *)preload_search_info(mod, MODINFO_TYPE); if (type == NULL) continue; if (strcmp(type, "md_image") && strcmp(type, "mfs_root")) continue; ptr = preload_fetch_addr(mod); len = preload_fetch_size(mod); if (ptr != NULL && len != 0) { sx_xlock(&md_sx); md_preloaded(ptr, len, name); sx_xunlock(&md_sx); } } md_vnode_pbuf_freecnt = nswbuf / 10; status_dev = make_dev(&mdctl_cdevsw, INT_MAX, UID_ROOT, GID_WHEEL, 0600, MDCTL_NAME); g_topology_lock(); } static void g_md_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp) { struct md_s *mp; char *type; mp = gp->softc; if (mp == NULL) return; switch (mp->type) { case MD_MALLOC: type = "malloc"; break; case MD_PRELOAD: type = "preload"; break; case MD_VNODE: type = "vnode"; break; case MD_SWAP: type = "swap"; break; case MD_NULL: type = "null"; break; default: type = "unknown"; break; } if (pp != NULL) { if (indent == NULL) { sbuf_printf(sb, " u %d", mp->unit); sbuf_printf(sb, " s %ju", (uintmax_t) mp->sectorsize); sbuf_printf(sb, " f %ju", (uintmax_t) mp->fwheads); sbuf_printf(sb, " fs %ju", (uintmax_t) mp->fwsectors); sbuf_printf(sb, " l %ju", (uintmax_t) mp->mediasize); sbuf_printf(sb, " t %s", type); if ((mp->type == MD_VNODE && mp->vnode != NULL) || (mp->type == MD_PRELOAD && mp->file[0] != '\0')) sbuf_printf(sb, " file %s", mp->file); sbuf_printf(sb, " label %s", mp->label); } else { sbuf_printf(sb, "%s%d\n", indent, mp->unit); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->sectorsize); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->fwheads); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->fwsectors); if (mp->ident[0] != '\0') { sbuf_printf(sb, "%s", indent); g_conf_printf_escaped(sb, "%s", mp->ident); sbuf_printf(sb, "\n"); } sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->mediasize); sbuf_printf(sb, "%s%s\n", indent, (mp->flags & MD_COMPRESS) == 0 ? "off": "on"); sbuf_printf(sb, "%s%s\n", indent, (mp->flags & MD_READONLY) == 0 ? "read-write": "read-only"); sbuf_printf(sb, "%s%s\n", indent, type); if ((mp->type == MD_VNODE && mp->vnode != NULL) || (mp->type == MD_PRELOAD && mp->file[0] != '\0')) { sbuf_printf(sb, "%s", indent); g_conf_printf_escaped(sb, "%s", mp->file); sbuf_printf(sb, "\n"); } sbuf_printf(sb, "%s\n"); } } } static void g_md_fini(struct g_class *mp __unused) { sx_destroy(&md_sx); if (status_dev != NULL) destroy_dev(status_dev); delete_unrhdr(md_uh); } Index: projects/clang700-import/sys/dev/rtwn/usb/rtwn_usb_attach.h =================================================================== --- projects/clang700-import/sys/dev/rtwn/usb/rtwn_usb_attach.h (revision 341812) +++ projects/clang700-import/sys/dev/rtwn/usb/rtwn_usb_attach.h (revision 341813) @@ -1,174 +1,175 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ void r92cu_attach(struct rtwn_usb_softc *); void r92eu_attach(struct rtwn_usb_softc *); void r88eu_attach(struct rtwn_usb_softc *); void r12au_attach(struct rtwn_usb_softc *); void r21au_attach(struct rtwn_usb_softc *); enum { RTWN_CHIP_RTL8192CU, RTWN_CHIP_RTL8192EU, RTWN_CHIP_RTL8188EU, RTWN_CHIP_RTL8812AU, RTWN_CHIP_RTL8821AU, RTWN_CHIP_MAX_USB }; /* various supported device vendors/products */ static const STRUCT_USB_HOST_ID rtwn_devs[] = { /* RTL8188CE-VAU/RTL8188CUS/RTL8188RU/RTL8192CU */ #define RTWN_RTL8192CU_DEV(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8192CU) } RTWN_RTL8192CU_DEV(ABOCOM, RTL8188CU_1), RTWN_RTL8192CU_DEV(ABOCOM, RTL8188CU_2), RTWN_RTL8192CU_DEV(ABOCOM, RTL8192CU), RTWN_RTL8192CU_DEV(ASUS, RTL8192CU), RTWN_RTL8192CU_DEV(ASUS, USBN10NANO), RTWN_RTL8192CU_DEV(AZUREWAVE, RTL8188CE_1), RTWN_RTL8192CU_DEV(AZUREWAVE, RTL8188CE_2), RTWN_RTL8192CU_DEV(AZUREWAVE, RTL8188CU), RTWN_RTL8192CU_DEV(BELKIN, F7D2102), RTWN_RTL8192CU_DEV(BELKIN, RTL8188CU), RTWN_RTL8192CU_DEV(BELKIN, RTL8192CU), RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_1), RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_2), RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_3), RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_4), RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_5), RTWN_RTL8192CU_DEV(COREGA, RTL8192CU), RTWN_RTL8192CU_DEV(DLINK, RTL8188CU), RTWN_RTL8192CU_DEV(DLINK, RTL8192CU_1), RTWN_RTL8192CU_DEV(DLINK, RTL8192CU_2), RTWN_RTL8192CU_DEV(DLINK, RTL8192CU_3), RTWN_RTL8192CU_DEV(DLINK, DWA131B), RTWN_RTL8192CU_DEV(EDIMAX, EW7811UN), RTWN_RTL8192CU_DEV(EDIMAX, RTL8192CU), RTWN_RTL8192CU_DEV(FEIXUN, RTL8188CU), RTWN_RTL8192CU_DEV(FEIXUN, RTL8192CU), RTWN_RTL8192CU_DEV(GUILLEMOT, HWNUP150), RTWN_RTL8192CU_DEV(HAWKING, RTL8192CU), RTWN_RTL8192CU_DEV(HP3, RTL8188CU), RTWN_RTL8192CU_DEV(NETGEAR, WNA1000M), RTWN_RTL8192CU_DEV(NETGEAR, RTL8192CU), RTWN_RTL8192CU_DEV(NETGEAR4, RTL8188CU), RTWN_RTL8192CU_DEV(NOVATECH, RTL8188CU), RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CU_1), RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CU_2), RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CU_3), RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CU_4), RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CUS), RTWN_RTL8192CU_DEV(PLANEX2, RTL8192CU), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CE_0), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CE_1), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CTV), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_0), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_1), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_2), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_3), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_COMBO), RTWN_RTL8192CU_DEV(REALTEK, RTL8188CUS), RTWN_RTL8192CU_DEV(REALTEK, RTL8188RU_1), RTWN_RTL8192CU_DEV(REALTEK, RTL8188RU_2), RTWN_RTL8192CU_DEV(REALTEK, RTL8188RU_3), RTWN_RTL8192CU_DEV(REALTEK, RTL8191CU), RTWN_RTL8192CU_DEV(REALTEK, RTL8192CE), RTWN_RTL8192CU_DEV(REALTEK, RTL8192CU), RTWN_RTL8192CU_DEV(SITECOMEU, RTL8188CU_1), RTWN_RTL8192CU_DEV(SITECOMEU, RTL8188CU_2), RTWN_RTL8192CU_DEV(SITECOMEU, RTL8192CU), RTWN_RTL8192CU_DEV(TRENDNET, RTL8188CU), RTWN_RTL8192CU_DEV(TRENDNET, RTL8192CU), RTWN_RTL8192CU_DEV(ZYXEL, RTL8192CU), #undef RTWN_RTL8192CU_DEV /* RTL8192EU */ #define RTWN_RTL8192EU_DEV(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8192EU) } RTWN_RTL8192EU_DEV(DLINK, DWA131E1), RTWN_RTL8192EU_DEV(REALTEK, RTL8192EU), RTWN_RTL8192EU_DEV(TPLINK, WN822NV4), RTWN_RTL8192EU_DEV(TPLINK, WN823NV2), #undef RTWN_RTL8192EU_DEV /* RTL8188EU */ #define RTWN_RTL8188EU_DEV(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8188EU) } RTWN_RTL8188EU_DEV(ABOCOM, RTL8188EU), RTWN_RTL8188EU_DEV(DLINK, DWA123D1), RTWN_RTL8188EU_DEV(DLINK, DWA125D1), RTWN_RTL8188EU_DEV(ELECOM, WDC150SU2M), + RTWN_RTL8188EU_DEV(TPLINK, WN722N), RTWN_RTL8188EU_DEV(REALTEK, RTL8188ETV), RTWN_RTL8188EU_DEV(REALTEK, RTL8188EU), #undef RTWN_RTL8188EU_DEV /* RTL8812AU */ #define RTWN_RTL8812AU_DEV(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8812AU) } RTWN_RTL8812AU_DEV(ASUS, USBAC56), RTWN_RTL8812AU_DEV(CISCOLINKSYS, WUSB6300), RTWN_RTL8812AU_DEV(DLINK, DWA182C1), RTWN_RTL8812AU_DEV(DLINK, DWA180A1), RTWN_RTL8812AU_DEV(EDIMAX, EW7822UAC), RTWN_RTL8812AU_DEV(IODATA, WNAC867U), RTWN_RTL8812AU_DEV(MELCO, WIU3866D), RTWN_RTL8812AU_DEV(NEC, WL900U), RTWN_RTL8812AU_DEV(PLANEX2, GW900D), RTWN_RTL8812AU_DEV(REALTEK, RTL8812AU), RTWN_RTL8812AU_DEV(SENAO, EUB1200AC), RTWN_RTL8812AU_DEV(SITECOMEU, WLA7100), RTWN_RTL8812AU_DEV(TPLINK, T4U), RTWN_RTL8812AU_DEV(TPLINK, T4UV2), RTWN_RTL8812AU_DEV(TPLINK, T4UHV2), RTWN_RTL8812AU_DEV(TRENDNET, TEW805UB), RTWN_RTL8812AU_DEV(ZYXEL, NWD6605), #undef RTWN_RTL8812AU_DEV /* RTL8821AU */ #define RTWN_RTL8821AU_DEV(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8821AU) } RTWN_RTL8821AU_DEV(DLINK, DWA171A1), RTWN_RTL8821AU_DEV(DLINK, DWA172A1), RTWN_RTL8821AU_DEV(EDIMAX, EW7811UTC_1), RTWN_RTL8821AU_DEV(EDIMAX, EW7811UTC_2), RTWN_RTL8821AU_DEV(HAWKING, HD65U), RTWN_RTL8821AU_DEV(MELCO, WIU2433DM), RTWN_RTL8821AU_DEV(NETGEAR, A6100) #undef RTWN_RTL8821AU_DEV }; typedef void (*chip_usb_attach)(struct rtwn_usb_softc *); static const chip_usb_attach rtwn_chip_usb_attach[RTWN_CHIP_MAX_USB] = { [RTWN_CHIP_RTL8192CU] = r92cu_attach, [RTWN_CHIP_RTL8192EU] = r92eu_attach, [RTWN_CHIP_RTL8188EU] = r88eu_attach, [RTWN_CHIP_RTL8812AU] = r12au_attach, [RTWN_CHIP_RTL8821AU] = r21au_attach }; static __inline void rtwn_usb_attach_private(struct rtwn_usb_softc *uc, int chip) { rtwn_chip_usb_attach[chip](uc); } Index: projects/clang700-import/sys/dev/sfxge/common/efx.h =================================================================== --- projects/clang700-import/sys/dev/sfxge/common/efx.h (revision 341812) +++ projects/clang700-import/sys/dev/sfxge/common/efx.h (revision 341813) @@ -1,3308 +1,3309 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006-2016 Solarflare Communications Inc. * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the FreeBSD Project. * * $FreeBSD$ */ #ifndef _SYS_EFX_H #define _SYS_EFX_H #include "efx_annote.h" #include "efsys.h" #include "efx_check.h" #include "efx_phy_ids.h" #ifdef __cplusplus extern "C" { #endif #define EFX_STATIC_ASSERT(_cond) \ ((void)sizeof (char[(_cond) ? 1 : -1])) #define EFX_ARRAY_SIZE(_array) \ (sizeof (_array) / sizeof ((_array)[0])) #define EFX_FIELD_OFFSET(_type, _field) \ ((size_t)&(((_type *)0)->_field)) /* The macro expands divider twice */ #define EFX_DIV_ROUND_UP(_n, _d) (((_n) + (_d) - 1) / (_d)) /* Return codes */ typedef __success(return == 0) int efx_rc_t; /* Chip families */ typedef enum efx_family_e { EFX_FAMILY_INVALID, EFX_FAMILY_FALCON, /* Obsolete and not supported */ EFX_FAMILY_SIENA, EFX_FAMILY_HUNTINGTON, EFX_FAMILY_MEDFORD, EFX_FAMILY_MEDFORD2, EFX_FAMILY_NTYPES } efx_family_t; extern __checkReturn efx_rc_t efx_family( __in uint16_t venid, __in uint16_t devid, __out efx_family_t *efp, __out unsigned int *membarp); #define EFX_PCI_VENID_SFC 0x1924 #define EFX_PCI_DEVID_FALCON 0x0710 /* SFC4000 */ #define EFX_PCI_DEVID_BETHPAGE 0x0803 /* SFC9020 */ #define EFX_PCI_DEVID_SIENA 0x0813 /* SFL9021 */ #define EFX_PCI_DEVID_SIENA_F1_UNINIT 0x0810 #define EFX_PCI_DEVID_HUNTINGTON_PF_UNINIT 0x0901 #define EFX_PCI_DEVID_FARMINGDALE 0x0903 /* SFC9120 PF */ #define EFX_PCI_DEVID_GREENPORT 0x0923 /* SFC9140 PF */ #define EFX_PCI_DEVID_FARMINGDALE_VF 0x1903 /* SFC9120 VF */ #define EFX_PCI_DEVID_GREENPORT_VF 0x1923 /* SFC9140 VF */ #define EFX_PCI_DEVID_MEDFORD_PF_UNINIT 0x0913 #define EFX_PCI_DEVID_MEDFORD 0x0A03 /* SFC9240 PF */ #define EFX_PCI_DEVID_MEDFORD_VF 0x1A03 /* SFC9240 VF */ #define EFX_PCI_DEVID_MEDFORD2_PF_UNINIT 0x0B13 #define EFX_PCI_DEVID_MEDFORD2 0x0B03 /* SFC9250 PF */ #define EFX_PCI_DEVID_MEDFORD2_VF 0x1B03 /* SFC9250 VF */ #define EFX_MEM_BAR_SIENA 2 #define EFX_MEM_BAR_HUNTINGTON_PF 2 #define EFX_MEM_BAR_HUNTINGTON_VF 0 #define EFX_MEM_BAR_MEDFORD_PF 2 #define EFX_MEM_BAR_MEDFORD_VF 0 #define EFX_MEM_BAR_MEDFORD2 0 /* Error codes */ enum { EFX_ERR_INVALID, EFX_ERR_SRAM_OOB, EFX_ERR_BUFID_DC_OOB, EFX_ERR_MEM_PERR, EFX_ERR_RBUF_OWN, EFX_ERR_TBUF_OWN, EFX_ERR_RDESQ_OWN, EFX_ERR_TDESQ_OWN, EFX_ERR_EVQ_OWN, EFX_ERR_EVFF_OFLO, EFX_ERR_ILL_ADDR, EFX_ERR_SRAM_PERR, EFX_ERR_NCODES }; /* Calculate the IEEE 802.3 CRC32 of a MAC addr */ extern __checkReturn uint32_t efx_crc32_calculate( __in uint32_t crc_init, __in_ecount(length) uint8_t const *input, __in int length); /* Type prototypes */ typedef struct efx_rxq_s efx_rxq_t; /* NIC */ typedef struct efx_nic_s efx_nic_t; extern __checkReturn efx_rc_t efx_nic_create( __in efx_family_t family, __in efsys_identifier_t *esip, __in efsys_bar_t *esbp, __in efsys_lock_t *eslp, __deref_out efx_nic_t **enpp); /* EFX_FW_VARIANT codes map one to one on MC_CMD_FW codes */ typedef enum efx_fw_variant_e { EFX_FW_VARIANT_FULL_FEATURED, EFX_FW_VARIANT_LOW_LATENCY, EFX_FW_VARIANT_PACKED_STREAM, EFX_FW_VARIANT_HIGH_TX_RATE, EFX_FW_VARIANT_PACKED_STREAM_HASH_MODE_1, EFX_FW_VARIANT_RULES_ENGINE, EFX_FW_VARIANT_DPDK, EFX_FW_VARIANT_DONT_CARE = 0xffffffff } efx_fw_variant_t; extern __checkReturn efx_rc_t efx_nic_probe( __in efx_nic_t *enp, __in efx_fw_variant_t efv); extern __checkReturn efx_rc_t efx_nic_init( __in efx_nic_t *enp); extern __checkReturn efx_rc_t efx_nic_reset( __in efx_nic_t *enp); extern __checkReturn boolean_t efx_nic_hw_unavailable( __in efx_nic_t *enp); extern void efx_nic_set_hw_unavailable( __in efx_nic_t *enp); #if EFSYS_OPT_DIAG extern __checkReturn efx_rc_t efx_nic_register_test( __in efx_nic_t *enp); #endif /* EFSYS_OPT_DIAG */ extern void efx_nic_fini( __in efx_nic_t *enp); extern void efx_nic_unprobe( __in efx_nic_t *enp); extern void efx_nic_destroy( __in efx_nic_t *enp); #define EFX_PCIE_LINK_SPEED_GEN1 1 #define EFX_PCIE_LINK_SPEED_GEN2 2 #define EFX_PCIE_LINK_SPEED_GEN3 3 typedef enum efx_pcie_link_performance_e { EFX_PCIE_LINK_PERFORMANCE_UNKNOWN_BANDWIDTH, EFX_PCIE_LINK_PERFORMANCE_SUBOPTIMAL_BANDWIDTH, EFX_PCIE_LINK_PERFORMANCE_SUBOPTIMAL_LATENCY, EFX_PCIE_LINK_PERFORMANCE_OPTIMAL } efx_pcie_link_performance_t; extern __checkReturn efx_rc_t efx_nic_calculate_pcie_link_bandwidth( __in uint32_t pcie_link_width, __in uint32_t pcie_link_gen, __out uint32_t *bandwidth_mbpsp); extern __checkReturn efx_rc_t efx_nic_check_pcie_link_speed( __in efx_nic_t *enp, __in uint32_t pcie_link_width, __in uint32_t pcie_link_gen, __out efx_pcie_link_performance_t *resultp); #if EFSYS_OPT_MCDI #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 /* Huntington and Medford require MCDIv2 commands */ #define WITH_MCDI_V2 1 #endif typedef struct efx_mcdi_req_s efx_mcdi_req_t; typedef enum efx_mcdi_exception_e { EFX_MCDI_EXCEPTION_MC_REBOOT, EFX_MCDI_EXCEPTION_MC_BADASSERT, } efx_mcdi_exception_t; #if EFSYS_OPT_MCDI_LOGGING typedef enum efx_log_msg_e { EFX_LOG_INVALID, EFX_LOG_MCDI_REQUEST, EFX_LOG_MCDI_RESPONSE, } efx_log_msg_t; #endif /* EFSYS_OPT_MCDI_LOGGING */ typedef struct efx_mcdi_transport_s { void *emt_context; efsys_mem_t *emt_dma_mem; void (*emt_execute)(void *, efx_mcdi_req_t *); void (*emt_ev_cpl)(void *); void (*emt_exception)(void *, efx_mcdi_exception_t); #if EFSYS_OPT_MCDI_LOGGING void (*emt_logger)(void *, efx_log_msg_t, void *, size_t, void *, size_t); #endif /* EFSYS_OPT_MCDI_LOGGING */ #if EFSYS_OPT_MCDI_PROXY_AUTH void (*emt_ev_proxy_response)(void *, uint32_t, efx_rc_t); #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */ } efx_mcdi_transport_t; extern __checkReturn efx_rc_t efx_mcdi_init( __in efx_nic_t *enp, __in const efx_mcdi_transport_t *mtp); extern __checkReturn efx_rc_t efx_mcdi_reboot( __in efx_nic_t *enp); void efx_mcdi_new_epoch( __in efx_nic_t *enp); extern void efx_mcdi_get_timeout( __in efx_nic_t *enp, __in efx_mcdi_req_t *emrp, __out uint32_t *usec_timeoutp); extern void efx_mcdi_request_start( __in efx_nic_t *enp, __in efx_mcdi_req_t *emrp, __in boolean_t ev_cpl); extern __checkReturn boolean_t efx_mcdi_request_poll( __in efx_nic_t *enp); extern __checkReturn boolean_t efx_mcdi_request_abort( __in efx_nic_t *enp); extern void efx_mcdi_fini( __in efx_nic_t *enp); #endif /* EFSYS_OPT_MCDI */ /* INTR */ #define EFX_NINTR_SIENA 1024 typedef enum efx_intr_type_e { EFX_INTR_INVALID = 0, EFX_INTR_LINE, EFX_INTR_MESSAGE, EFX_INTR_NTYPES } efx_intr_type_t; #define EFX_INTR_SIZE (sizeof (efx_oword_t)) extern __checkReturn efx_rc_t efx_intr_init( __in efx_nic_t *enp, __in efx_intr_type_t type, __in_opt efsys_mem_t *esmp); extern void efx_intr_enable( __in efx_nic_t *enp); extern void efx_intr_disable( __in efx_nic_t *enp); extern void efx_intr_disable_unlocked( __in efx_nic_t *enp); #define EFX_INTR_NEVQS 32 extern __checkReturn efx_rc_t efx_intr_trigger( __in efx_nic_t *enp, __in unsigned int level); extern void efx_intr_status_line( __in efx_nic_t *enp, __out boolean_t *fatalp, __out uint32_t *maskp); extern void efx_intr_status_message( __in efx_nic_t *enp, __in unsigned int message, __out boolean_t *fatalp); extern void efx_intr_fatal( __in efx_nic_t *enp); extern void efx_intr_fini( __in efx_nic_t *enp); /* MAC */ #if EFSYS_OPT_MAC_STATS /* START MKCONFIG GENERATED EfxHeaderMacBlock ea466a9bc8789994 */ typedef enum efx_mac_stat_e { EFX_MAC_RX_OCTETS, EFX_MAC_RX_PKTS, EFX_MAC_RX_UNICST_PKTS, EFX_MAC_RX_MULTICST_PKTS, EFX_MAC_RX_BRDCST_PKTS, EFX_MAC_RX_PAUSE_PKTS, EFX_MAC_RX_LE_64_PKTS, EFX_MAC_RX_65_TO_127_PKTS, EFX_MAC_RX_128_TO_255_PKTS, EFX_MAC_RX_256_TO_511_PKTS, EFX_MAC_RX_512_TO_1023_PKTS, EFX_MAC_RX_1024_TO_15XX_PKTS, EFX_MAC_RX_GE_15XX_PKTS, EFX_MAC_RX_ERRORS, EFX_MAC_RX_FCS_ERRORS, EFX_MAC_RX_DROP_EVENTS, EFX_MAC_RX_FALSE_CARRIER_ERRORS, EFX_MAC_RX_SYMBOL_ERRORS, EFX_MAC_RX_ALIGN_ERRORS, EFX_MAC_RX_INTERNAL_ERRORS, EFX_MAC_RX_JABBER_PKTS, EFX_MAC_RX_LANE0_CHAR_ERR, EFX_MAC_RX_LANE1_CHAR_ERR, EFX_MAC_RX_LANE2_CHAR_ERR, EFX_MAC_RX_LANE3_CHAR_ERR, EFX_MAC_RX_LANE0_DISP_ERR, EFX_MAC_RX_LANE1_DISP_ERR, EFX_MAC_RX_LANE2_DISP_ERR, EFX_MAC_RX_LANE3_DISP_ERR, EFX_MAC_RX_MATCH_FAULT, EFX_MAC_RX_NODESC_DROP_CNT, EFX_MAC_TX_OCTETS, EFX_MAC_TX_PKTS, EFX_MAC_TX_UNICST_PKTS, EFX_MAC_TX_MULTICST_PKTS, EFX_MAC_TX_BRDCST_PKTS, EFX_MAC_TX_PAUSE_PKTS, EFX_MAC_TX_LE_64_PKTS, EFX_MAC_TX_65_TO_127_PKTS, EFX_MAC_TX_128_TO_255_PKTS, EFX_MAC_TX_256_TO_511_PKTS, EFX_MAC_TX_512_TO_1023_PKTS, EFX_MAC_TX_1024_TO_15XX_PKTS, EFX_MAC_TX_GE_15XX_PKTS, EFX_MAC_TX_ERRORS, EFX_MAC_TX_SGL_COL_PKTS, EFX_MAC_TX_MULT_COL_PKTS, EFX_MAC_TX_EX_COL_PKTS, EFX_MAC_TX_LATE_COL_PKTS, EFX_MAC_TX_DEF_PKTS, EFX_MAC_TX_EX_DEF_PKTS, EFX_MAC_PM_TRUNC_BB_OVERFLOW, EFX_MAC_PM_DISCARD_BB_OVERFLOW, EFX_MAC_PM_TRUNC_VFIFO_FULL, EFX_MAC_PM_DISCARD_VFIFO_FULL, EFX_MAC_PM_TRUNC_QBB, EFX_MAC_PM_DISCARD_QBB, EFX_MAC_PM_DISCARD_MAPPING, EFX_MAC_RXDP_Q_DISABLED_PKTS, EFX_MAC_RXDP_DI_DROPPED_PKTS, EFX_MAC_RXDP_STREAMING_PKTS, EFX_MAC_RXDP_HLB_FETCH, EFX_MAC_RXDP_HLB_WAIT, EFX_MAC_VADAPTER_RX_UNICAST_PACKETS, EFX_MAC_VADAPTER_RX_UNICAST_BYTES, EFX_MAC_VADAPTER_RX_MULTICAST_PACKETS, EFX_MAC_VADAPTER_RX_MULTICAST_BYTES, EFX_MAC_VADAPTER_RX_BROADCAST_PACKETS, EFX_MAC_VADAPTER_RX_BROADCAST_BYTES, EFX_MAC_VADAPTER_RX_BAD_PACKETS, EFX_MAC_VADAPTER_RX_BAD_BYTES, EFX_MAC_VADAPTER_RX_OVERFLOW, EFX_MAC_VADAPTER_TX_UNICAST_PACKETS, EFX_MAC_VADAPTER_TX_UNICAST_BYTES, EFX_MAC_VADAPTER_TX_MULTICAST_PACKETS, EFX_MAC_VADAPTER_TX_MULTICAST_BYTES, EFX_MAC_VADAPTER_TX_BROADCAST_PACKETS, EFX_MAC_VADAPTER_TX_BROADCAST_BYTES, EFX_MAC_VADAPTER_TX_BAD_PACKETS, EFX_MAC_VADAPTER_TX_BAD_BYTES, EFX_MAC_VADAPTER_TX_OVERFLOW, EFX_MAC_FEC_UNCORRECTED_ERRORS, EFX_MAC_FEC_CORRECTED_ERRORS, EFX_MAC_FEC_CORRECTED_SYMBOLS_LANE0, EFX_MAC_FEC_CORRECTED_SYMBOLS_LANE1, EFX_MAC_FEC_CORRECTED_SYMBOLS_LANE2, EFX_MAC_FEC_CORRECTED_SYMBOLS_LANE3, EFX_MAC_CTPIO_VI_BUSY_FALLBACK, EFX_MAC_CTPIO_LONG_WRITE_SUCCESS, EFX_MAC_CTPIO_MISSING_DBELL_FAIL, EFX_MAC_CTPIO_OVERFLOW_FAIL, EFX_MAC_CTPIO_UNDERFLOW_FAIL, EFX_MAC_CTPIO_TIMEOUT_FAIL, EFX_MAC_CTPIO_NONCONTIG_WR_FAIL, EFX_MAC_CTPIO_FRM_CLOBBER_FAIL, EFX_MAC_CTPIO_INVALID_WR_FAIL, EFX_MAC_CTPIO_VI_CLOBBER_FALLBACK, EFX_MAC_CTPIO_UNQUALIFIED_FALLBACK, EFX_MAC_CTPIO_RUNT_FALLBACK, EFX_MAC_CTPIO_SUCCESS, EFX_MAC_CTPIO_FALLBACK, EFX_MAC_CTPIO_POISON, EFX_MAC_CTPIO_ERASE, EFX_MAC_RXDP_SCATTER_DISABLED_TRUNC, EFX_MAC_RXDP_HLB_IDLE, EFX_MAC_RXDP_HLB_TIMEOUT, EFX_MAC_NSTATS } efx_mac_stat_t; /* END MKCONFIG GENERATED EfxHeaderMacBlock */ #endif /* EFSYS_OPT_MAC_STATS */ typedef enum efx_link_mode_e { EFX_LINK_UNKNOWN = 0, EFX_LINK_DOWN, EFX_LINK_10HDX, EFX_LINK_10FDX, EFX_LINK_100HDX, EFX_LINK_100FDX, EFX_LINK_1000HDX, EFX_LINK_1000FDX, EFX_LINK_10000FDX, EFX_LINK_40000FDX, EFX_LINK_25000FDX, EFX_LINK_50000FDX, EFX_LINK_100000FDX, EFX_LINK_NMODES } efx_link_mode_t; #define EFX_MAC_ADDR_LEN 6 #define EFX_VNI_OR_VSID_LEN 3 #define EFX_MAC_ADDR_IS_MULTICAST(_address) (((uint8_t *)_address)[0] & 0x01) #define EFX_MAC_MULTICAST_LIST_MAX 256 #define EFX_MAC_SDU_MAX 9202 #define EFX_MAC_PDU_ADJUSTMENT \ (/* EtherII */ 14 \ + /* VLAN */ 4 \ + /* CRC */ 4 \ + /* bug16011 */ 16) \ #define EFX_MAC_PDU(_sdu) \ P2ROUNDUP((_sdu) + EFX_MAC_PDU_ADJUSTMENT, 8) /* * Due to the P2ROUNDUP in EFX_MAC_PDU(), EFX_MAC_SDU_FROM_PDU() may give * the SDU rounded up slightly. */ #define EFX_MAC_SDU_FROM_PDU(_pdu) ((_pdu) - EFX_MAC_PDU_ADJUSTMENT) #define EFX_MAC_PDU_MIN 60 #define EFX_MAC_PDU_MAX EFX_MAC_PDU(EFX_MAC_SDU_MAX) extern __checkReturn efx_rc_t efx_mac_pdu_get( __in efx_nic_t *enp, __out size_t *pdu); extern __checkReturn efx_rc_t efx_mac_pdu_set( __in efx_nic_t *enp, __in size_t pdu); extern __checkReturn efx_rc_t efx_mac_addr_set( __in efx_nic_t *enp, __in uint8_t *addr); extern __checkReturn efx_rc_t efx_mac_filter_set( __in efx_nic_t *enp, __in boolean_t all_unicst, __in boolean_t mulcst, __in boolean_t all_mulcst, __in boolean_t brdcst); extern __checkReturn efx_rc_t efx_mac_multicast_list_set( __in efx_nic_t *enp, __in_ecount(6*count) uint8_t const *addrs, __in int count); extern __checkReturn efx_rc_t efx_mac_filter_default_rxq_set( __in efx_nic_t *enp, __in efx_rxq_t *erp, __in boolean_t using_rss); extern void efx_mac_filter_default_rxq_clear( __in efx_nic_t *enp); extern __checkReturn efx_rc_t efx_mac_drain( __in efx_nic_t *enp, __in boolean_t enabled); extern __checkReturn efx_rc_t efx_mac_up( __in efx_nic_t *enp, __out boolean_t *mac_upp); #define EFX_FCNTL_RESPOND 0x00000001 #define EFX_FCNTL_GENERATE 0x00000002 extern __checkReturn efx_rc_t efx_mac_fcntl_set( __in efx_nic_t *enp, __in unsigned int fcntl, __in boolean_t autoneg); extern void efx_mac_fcntl_get( __in efx_nic_t *enp, __out unsigned int *fcntl_wantedp, __out unsigned int *fcntl_linkp); #if EFSYS_OPT_MAC_STATS #if EFSYS_OPT_NAMES extern __checkReturn const char * efx_mac_stat_name( __in efx_nic_t *enp, __in unsigned int id); #endif /* EFSYS_OPT_NAMES */ #define EFX_MAC_STATS_MASK_BITS_PER_PAGE (8 * sizeof (uint32_t)) #define EFX_MAC_STATS_MASK_NPAGES \ (P2ROUNDUP(EFX_MAC_NSTATS, EFX_MAC_STATS_MASK_BITS_PER_PAGE) / \ EFX_MAC_STATS_MASK_BITS_PER_PAGE) /* * Get mask of MAC statistics supported by the hardware. * * If mask_size is insufficient to return the mask, EINVAL error is * returned. EFX_MAC_STATS_MASK_NPAGES multiplied by size of the page * (which is sizeof (uint32_t)) is sufficient. */ extern __checkReturn efx_rc_t efx_mac_stats_get_mask( __in efx_nic_t *enp, __out_bcount(mask_size) uint32_t *maskp, __in size_t mask_size); #define EFX_MAC_STAT_SUPPORTED(_mask, _stat) \ ((_mask)[(_stat) / EFX_MAC_STATS_MASK_BITS_PER_PAGE] & \ (1ULL << ((_stat) & (EFX_MAC_STATS_MASK_BITS_PER_PAGE - 1)))) extern __checkReturn efx_rc_t efx_mac_stats_clear( __in efx_nic_t *enp); /* * Upload mac statistics supported by the hardware into the given buffer. * * The DMA buffer must be 4Kbyte aligned and sized to hold at least * efx_nic_cfg_t::enc_mac_stats_nstats 64bit counters. * * The hardware will only DMA statistics that it understands (of course). * Drivers should not make any assumptions about which statistics are * supported, especially when the statistics are generated by firmware. * * Thus, drivers should zero this buffer before use, so that not-understood * statistics read back as zero. */ extern __checkReturn efx_rc_t efx_mac_stats_upload( __in efx_nic_t *enp, __in efsys_mem_t *esmp); extern __checkReturn efx_rc_t efx_mac_stats_periodic( __in efx_nic_t *enp, __in efsys_mem_t *esmp, __in uint16_t period_ms, __in boolean_t events); extern __checkReturn efx_rc_t efx_mac_stats_update( __in efx_nic_t *enp, __in efsys_mem_t *esmp, __inout_ecount(EFX_MAC_NSTATS) efsys_stat_t *stat, __inout_opt uint32_t *generationp); #endif /* EFSYS_OPT_MAC_STATS */ /* MON */ typedef enum efx_mon_type_e { EFX_MON_INVALID = 0, EFX_MON_SFC90X0, EFX_MON_SFC91X0, EFX_MON_SFC92X0, EFX_MON_NTYPES } efx_mon_type_t; #if EFSYS_OPT_NAMES extern const char * efx_mon_name( __in efx_nic_t *enp); #endif /* EFSYS_OPT_NAMES */ extern __checkReturn efx_rc_t efx_mon_init( __in efx_nic_t *enp); #if EFSYS_OPT_MON_STATS #define EFX_MON_STATS_PAGE_SIZE 0x100 #define EFX_MON_MASK_ELEMENT_SIZE 32 /* START MKCONFIG GENERATED MonitorHeaderStatsBlock 78b65c8d5af9747b */ typedef enum efx_mon_stat_e { EFX_MON_STAT_CONTROLLER_TEMP, EFX_MON_STAT_PHY_COMMON_TEMP, EFX_MON_STAT_CONTROLLER_COOLING, EFX_MON_STAT_PHY0_TEMP, EFX_MON_STAT_PHY0_COOLING, EFX_MON_STAT_PHY1_TEMP, EFX_MON_STAT_PHY1_COOLING, EFX_MON_STAT_IN_1V0, EFX_MON_STAT_IN_1V2, EFX_MON_STAT_IN_1V8, EFX_MON_STAT_IN_2V5, EFX_MON_STAT_IN_3V3, EFX_MON_STAT_IN_12V0, EFX_MON_STAT_IN_1V2A, EFX_MON_STAT_IN_VREF, EFX_MON_STAT_OUT_VAOE, EFX_MON_STAT_AOE_TEMP, EFX_MON_STAT_PSU_AOE_TEMP, EFX_MON_STAT_PSU_TEMP, EFX_MON_STAT_FAN_0, EFX_MON_STAT_FAN_1, EFX_MON_STAT_FAN_2, EFX_MON_STAT_FAN_3, EFX_MON_STAT_FAN_4, EFX_MON_STAT_IN_VAOE, EFX_MON_STAT_OUT_IAOE, EFX_MON_STAT_IN_IAOE, EFX_MON_STAT_NIC_POWER, EFX_MON_STAT_IN_0V9, EFX_MON_STAT_IN_I0V9, EFX_MON_STAT_IN_I1V2, EFX_MON_STAT_IN_0V9_ADC, EFX_MON_STAT_CONTROLLER_2_TEMP, EFX_MON_STAT_VREG_INTERNAL_TEMP, EFX_MON_STAT_VREG_0V9_TEMP, EFX_MON_STAT_VREG_1V2_TEMP, EFX_MON_STAT_CONTROLLER_VPTAT, EFX_MON_STAT_CONTROLLER_INTERNAL_TEMP, EFX_MON_STAT_CONTROLLER_VPTAT_EXTADC, EFX_MON_STAT_CONTROLLER_INTERNAL_TEMP_EXTADC, EFX_MON_STAT_AMBIENT_TEMP, EFX_MON_STAT_AIRFLOW, EFX_MON_STAT_VDD08D_VSS08D_CSR, EFX_MON_STAT_VDD08D_VSS08D_CSR_EXTADC, EFX_MON_STAT_HOTPOINT_TEMP, EFX_MON_STAT_PHY_POWER_PORT0, EFX_MON_STAT_PHY_POWER_PORT1, EFX_MON_STAT_MUM_VCC, EFX_MON_STAT_IN_0V9_A, EFX_MON_STAT_IN_I0V9_A, EFX_MON_STAT_VREG_0V9_A_TEMP, EFX_MON_STAT_IN_0V9_B, EFX_MON_STAT_IN_I0V9_B, EFX_MON_STAT_VREG_0V9_B_TEMP, EFX_MON_STAT_CCOM_AVREG_1V2_SUPPLY, EFX_MON_STAT_CCOM_AVREG_1V2_SUPPLY_EXTADC, EFX_MON_STAT_CCOM_AVREG_1V8_SUPPLY, EFX_MON_STAT_CCOM_AVREG_1V8_SUPPLY_EXTADC, EFX_MON_STAT_CONTROLLER_MASTER_VPTAT, EFX_MON_STAT_CONTROLLER_MASTER_INTERNAL_TEMP, EFX_MON_STAT_CONTROLLER_MASTER_VPTAT_EXTADC, EFX_MON_STAT_CONTROLLER_MASTER_INTERNAL_TEMP_EXTADC, EFX_MON_STAT_CONTROLLER_SLAVE_VPTAT, EFX_MON_STAT_CONTROLLER_SLAVE_INTERNAL_TEMP, EFX_MON_STAT_CONTROLLER_SLAVE_VPTAT_EXTADC, EFX_MON_STAT_CONTROLLER_SLAVE_INTERNAL_TEMP_EXTADC, EFX_MON_STAT_SODIMM_VOUT, EFX_MON_STAT_SODIMM_0_TEMP, EFX_MON_STAT_SODIMM_1_TEMP, EFX_MON_STAT_PHY0_VCC, EFX_MON_STAT_PHY1_VCC, EFX_MON_STAT_CONTROLLER_TDIODE_TEMP, EFX_MON_STAT_BOARD_FRONT_TEMP, EFX_MON_STAT_BOARD_BACK_TEMP, EFX_MON_STAT_IN_I1V8, EFX_MON_STAT_IN_I2V5, EFX_MON_STAT_IN_I3V3, EFX_MON_STAT_IN_I12V0, EFX_MON_STAT_IN_1V3, EFX_MON_STAT_IN_I1V3, EFX_MON_NSTATS } efx_mon_stat_t; /* END MKCONFIG GENERATED MonitorHeaderStatsBlock */ typedef enum efx_mon_stat_state_e { EFX_MON_STAT_STATE_OK = 0, EFX_MON_STAT_STATE_WARNING = 1, EFX_MON_STAT_STATE_FATAL = 2, EFX_MON_STAT_STATE_BROKEN = 3, EFX_MON_STAT_STATE_NO_READING = 4, } efx_mon_stat_state_t; typedef enum efx_mon_stat_unit_e { EFX_MON_STAT_UNIT_UNKNOWN = 0, EFX_MON_STAT_UNIT_BOOL, EFX_MON_STAT_UNIT_TEMP_C, EFX_MON_STAT_UNIT_VOLTAGE_MV, EFX_MON_STAT_UNIT_CURRENT_MA, EFX_MON_STAT_UNIT_POWER_W, EFX_MON_STAT_UNIT_RPM, EFX_MON_NUNITS } efx_mon_stat_unit_t; typedef struct efx_mon_stat_value_s { uint16_t emsv_value; efx_mon_stat_state_t emsv_state; efx_mon_stat_unit_t emsv_unit; } efx_mon_stat_value_t; typedef struct efx_mon_limit_value_s { uint16_t emlv_warning_min; uint16_t emlv_warning_max; uint16_t emlv_fatal_min; uint16_t emlv_fatal_max; } efx_mon_stat_limits_t; typedef enum efx_mon_stat_portmask_e { EFX_MON_STAT_PORTMAP_NONE = 0, EFX_MON_STAT_PORTMAP_PORT0 = 1, EFX_MON_STAT_PORTMAP_PORT1 = 2, EFX_MON_STAT_PORTMAP_PORT2 = 3, EFX_MON_STAT_PORTMAP_PORT3 = 4, EFX_MON_STAT_PORTMAP_ALL = (-1), EFX_MON_STAT_PORTMAP_UNKNOWN = (-2) } efx_mon_stat_portmask_t; #if EFSYS_OPT_NAMES extern const char * efx_mon_stat_name( __in efx_nic_t *enp, __in efx_mon_stat_t id); extern const char * efx_mon_stat_description( __in efx_nic_t *enp, __in efx_mon_stat_t id); #endif /* EFSYS_OPT_NAMES */ extern __checkReturn boolean_t efx_mon_mcdi_to_efx_stat( __in int mcdi_index, __out efx_mon_stat_t *statp); extern __checkReturn boolean_t efx_mon_get_stat_unit( __in efx_mon_stat_t stat, __out efx_mon_stat_unit_t *unitp); extern __checkReturn boolean_t efx_mon_get_stat_portmap( __in efx_mon_stat_t stat, __out efx_mon_stat_portmask_t *maskp); extern __checkReturn efx_rc_t efx_mon_stats_update( __in efx_nic_t *enp, __in efsys_mem_t *esmp, __inout_ecount(EFX_MON_NSTATS) efx_mon_stat_value_t *values); extern __checkReturn efx_rc_t efx_mon_limits_update( __in efx_nic_t *enp, __inout_ecount(EFX_MON_NSTATS) efx_mon_stat_limits_t *values); #endif /* EFSYS_OPT_MON_STATS */ extern void efx_mon_fini( __in efx_nic_t *enp); /* PHY */ extern __checkReturn efx_rc_t efx_phy_verify( __in efx_nic_t *enp); #if EFSYS_OPT_PHY_LED_CONTROL typedef enum efx_phy_led_mode_e { EFX_PHY_LED_DEFAULT = 0, EFX_PHY_LED_OFF, EFX_PHY_LED_ON, EFX_PHY_LED_FLASH, EFX_PHY_LED_NMODES } efx_phy_led_mode_t; extern __checkReturn efx_rc_t efx_phy_led_set( __in efx_nic_t *enp, __in efx_phy_led_mode_t mode); #endif /* EFSYS_OPT_PHY_LED_CONTROL */ extern __checkReturn efx_rc_t efx_port_init( __in efx_nic_t *enp); #if EFSYS_OPT_LOOPBACK typedef enum efx_loopback_type_e { EFX_LOOPBACK_OFF = 0, EFX_LOOPBACK_DATA = 1, EFX_LOOPBACK_GMAC = 2, EFX_LOOPBACK_XGMII = 3, EFX_LOOPBACK_XGXS = 4, EFX_LOOPBACK_XAUI = 5, EFX_LOOPBACK_GMII = 6, EFX_LOOPBACK_SGMII = 7, EFX_LOOPBACK_XGBR = 8, EFX_LOOPBACK_XFI = 9, EFX_LOOPBACK_XAUI_FAR = 10, EFX_LOOPBACK_GMII_FAR = 11, EFX_LOOPBACK_SGMII_FAR = 12, EFX_LOOPBACK_XFI_FAR = 13, EFX_LOOPBACK_GPHY = 14, EFX_LOOPBACK_PHY_XS = 15, EFX_LOOPBACK_PCS = 16, EFX_LOOPBACK_PMA_PMD = 17, EFX_LOOPBACK_XPORT = 18, EFX_LOOPBACK_XGMII_WS = 19, EFX_LOOPBACK_XAUI_WS = 20, EFX_LOOPBACK_XAUI_WS_FAR = 21, EFX_LOOPBACK_XAUI_WS_NEAR = 22, EFX_LOOPBACK_GMII_WS = 23, EFX_LOOPBACK_XFI_WS = 24, EFX_LOOPBACK_XFI_WS_FAR = 25, EFX_LOOPBACK_PHYXS_WS = 26, EFX_LOOPBACK_PMA_INT = 27, EFX_LOOPBACK_SD_NEAR = 28, EFX_LOOPBACK_SD_FAR = 29, EFX_LOOPBACK_PMA_INT_WS = 30, EFX_LOOPBACK_SD_FEP2_WS = 31, EFX_LOOPBACK_SD_FEP1_5_WS = 32, EFX_LOOPBACK_SD_FEP_WS = 33, EFX_LOOPBACK_SD_FES_WS = 34, EFX_LOOPBACK_AOE_INT_NEAR = 35, EFX_LOOPBACK_DATA_WS = 36, EFX_LOOPBACK_FORCE_EXT_LINK = 37, EFX_LOOPBACK_NTYPES } efx_loopback_type_t; typedef enum efx_loopback_kind_e { EFX_LOOPBACK_KIND_OFF = 0, EFX_LOOPBACK_KIND_ALL, EFX_LOOPBACK_KIND_MAC, EFX_LOOPBACK_KIND_PHY, EFX_LOOPBACK_NKINDS } efx_loopback_kind_t; extern void efx_loopback_mask( __in efx_loopback_kind_t loopback_kind, __out efx_qword_t *maskp); extern __checkReturn efx_rc_t efx_port_loopback_set( __in efx_nic_t *enp, __in efx_link_mode_t link_mode, __in efx_loopback_type_t type); #if EFSYS_OPT_NAMES extern __checkReturn const char * efx_loopback_type_name( __in efx_nic_t *enp, __in efx_loopback_type_t type); #endif /* EFSYS_OPT_NAMES */ #endif /* EFSYS_OPT_LOOPBACK */ extern __checkReturn efx_rc_t efx_port_poll( __in efx_nic_t *enp, __out_opt efx_link_mode_t *link_modep); extern void efx_port_fini( __in efx_nic_t *enp); typedef enum efx_phy_cap_type_e { EFX_PHY_CAP_INVALID = 0, EFX_PHY_CAP_10HDX, EFX_PHY_CAP_10FDX, EFX_PHY_CAP_100HDX, EFX_PHY_CAP_100FDX, EFX_PHY_CAP_1000HDX, EFX_PHY_CAP_1000FDX, EFX_PHY_CAP_10000FDX, EFX_PHY_CAP_PAUSE, EFX_PHY_CAP_ASYM, EFX_PHY_CAP_AN, EFX_PHY_CAP_40000FDX, EFX_PHY_CAP_DDM, EFX_PHY_CAP_100000FDX, EFX_PHY_CAP_25000FDX, EFX_PHY_CAP_50000FDX, EFX_PHY_CAP_BASER_FEC, EFX_PHY_CAP_BASER_FEC_REQUESTED, EFX_PHY_CAP_RS_FEC, EFX_PHY_CAP_RS_FEC_REQUESTED, EFX_PHY_CAP_25G_BASER_FEC, EFX_PHY_CAP_25G_BASER_FEC_REQUESTED, EFX_PHY_CAP_NTYPES } efx_phy_cap_type_t; #define EFX_PHY_CAP_CURRENT 0x00000000 #define EFX_PHY_CAP_DEFAULT 0x00000001 #define EFX_PHY_CAP_PERM 0x00000002 extern void efx_phy_adv_cap_get( __in efx_nic_t *enp, __in uint32_t flag, __out uint32_t *maskp); extern __checkReturn efx_rc_t efx_phy_adv_cap_set( __in efx_nic_t *enp, __in uint32_t mask); extern void efx_phy_lp_cap_get( __in efx_nic_t *enp, __out uint32_t *maskp); extern __checkReturn efx_rc_t efx_phy_oui_get( __in efx_nic_t *enp, __out uint32_t *ouip); typedef enum efx_phy_media_type_e { EFX_PHY_MEDIA_INVALID = 0, EFX_PHY_MEDIA_XAUI, EFX_PHY_MEDIA_CX4, EFX_PHY_MEDIA_KX4, EFX_PHY_MEDIA_XFP, EFX_PHY_MEDIA_SFP_PLUS, EFX_PHY_MEDIA_BASE_T, EFX_PHY_MEDIA_QSFP_PLUS, EFX_PHY_MEDIA_NTYPES } efx_phy_media_type_t; /* * Get the type of medium currently used. If the board has ports for * modules, a module is present, and we recognise the media type of * the module, then this will be the media type of the module. * Otherwise it will be the media type of the port. */ extern void efx_phy_media_type_get( __in efx_nic_t *enp, __out efx_phy_media_type_t *typep); /* * 2-wire device address of the base information in accordance with SFF-8472 * Diagnostic Monitoring Interface for Optical Transceivers section * 4 Memory Organization. */ #define EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE 0xA0 /* * 2-wire device address of the digital diagnostics monitoring interface * in accordance with SFF-8472 Diagnostic Monitoring Interface for Optical * Transceivers section 4 Memory Organization. */ #define EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM 0xA2 /* * Hard wired 2-wire device address for QSFP+ in accordance with SFF-8436 * QSFP+ 10 Gbs 4X PLUGGABLE TRANSCEIVER section 7.4 Device Addressing and * Operation. */ #define EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP 0xA0 /* * Maximum accessible data offset for PHY module information. */ #define EFX_PHY_MEDIA_INFO_MAX_OFFSET 0x100 extern __checkReturn efx_rc_t efx_phy_module_get_info( __in efx_nic_t *enp, __in uint8_t dev_addr, __in size_t offset, __in size_t len, __out_bcount(len) uint8_t *data); #if EFSYS_OPT_PHY_STATS /* START MKCONFIG GENERATED PhyHeaderStatsBlock 30ed56ad501f8e36 */ typedef enum efx_phy_stat_e { EFX_PHY_STAT_OUI, EFX_PHY_STAT_PMA_PMD_LINK_UP, EFX_PHY_STAT_PMA_PMD_RX_FAULT, EFX_PHY_STAT_PMA_PMD_TX_FAULT, EFX_PHY_STAT_PMA_PMD_REV_A, EFX_PHY_STAT_PMA_PMD_REV_B, EFX_PHY_STAT_PMA_PMD_REV_C, EFX_PHY_STAT_PMA_PMD_REV_D, EFX_PHY_STAT_PCS_LINK_UP, EFX_PHY_STAT_PCS_RX_FAULT, EFX_PHY_STAT_PCS_TX_FAULT, EFX_PHY_STAT_PCS_BER, EFX_PHY_STAT_PCS_BLOCK_ERRORS, EFX_PHY_STAT_PHY_XS_LINK_UP, EFX_PHY_STAT_PHY_XS_RX_FAULT, EFX_PHY_STAT_PHY_XS_TX_FAULT, EFX_PHY_STAT_PHY_XS_ALIGN, EFX_PHY_STAT_PHY_XS_SYNC_A, EFX_PHY_STAT_PHY_XS_SYNC_B, EFX_PHY_STAT_PHY_XS_SYNC_C, EFX_PHY_STAT_PHY_XS_SYNC_D, EFX_PHY_STAT_AN_LINK_UP, EFX_PHY_STAT_AN_MASTER, EFX_PHY_STAT_AN_LOCAL_RX_OK, EFX_PHY_STAT_AN_REMOTE_RX_OK, EFX_PHY_STAT_CL22EXT_LINK_UP, EFX_PHY_STAT_SNR_A, EFX_PHY_STAT_SNR_B, EFX_PHY_STAT_SNR_C, EFX_PHY_STAT_SNR_D, EFX_PHY_STAT_PMA_PMD_SIGNAL_A, EFX_PHY_STAT_PMA_PMD_SIGNAL_B, EFX_PHY_STAT_PMA_PMD_SIGNAL_C, EFX_PHY_STAT_PMA_PMD_SIGNAL_D, EFX_PHY_STAT_AN_COMPLETE, EFX_PHY_STAT_PMA_PMD_REV_MAJOR, EFX_PHY_STAT_PMA_PMD_REV_MINOR, EFX_PHY_STAT_PMA_PMD_REV_MICRO, EFX_PHY_STAT_PCS_FW_VERSION_0, EFX_PHY_STAT_PCS_FW_VERSION_1, EFX_PHY_STAT_PCS_FW_VERSION_2, EFX_PHY_STAT_PCS_FW_VERSION_3, EFX_PHY_STAT_PCS_FW_BUILD_YY, EFX_PHY_STAT_PCS_FW_BUILD_MM, EFX_PHY_STAT_PCS_FW_BUILD_DD, EFX_PHY_STAT_PCS_OP_MODE, EFX_PHY_NSTATS } efx_phy_stat_t; /* END MKCONFIG GENERATED PhyHeaderStatsBlock */ #if EFSYS_OPT_NAMES extern const char * efx_phy_stat_name( __in efx_nic_t *enp, __in efx_phy_stat_t stat); #endif /* EFSYS_OPT_NAMES */ #define EFX_PHY_STATS_SIZE 0x100 extern __checkReturn efx_rc_t efx_phy_stats_update( __in efx_nic_t *enp, __in efsys_mem_t *esmp, __inout_ecount(EFX_PHY_NSTATS) uint32_t *stat); #endif /* EFSYS_OPT_PHY_STATS */ #if EFSYS_OPT_BIST typedef enum efx_bist_type_e { EFX_BIST_TYPE_UNKNOWN, EFX_BIST_TYPE_PHY_NORMAL, EFX_BIST_TYPE_PHY_CABLE_SHORT, EFX_BIST_TYPE_PHY_CABLE_LONG, EFX_BIST_TYPE_MC_MEM, /* Test the MC DMEM and IMEM */ EFX_BIST_TYPE_SAT_MEM, /* Test the DMEM and IMEM of satellite cpus */ EFX_BIST_TYPE_REG, /* Test the register memories */ EFX_BIST_TYPE_NTYPES, } efx_bist_type_t; typedef enum efx_bist_result_e { EFX_BIST_RESULT_UNKNOWN, EFX_BIST_RESULT_RUNNING, EFX_BIST_RESULT_PASSED, EFX_BIST_RESULT_FAILED, } efx_bist_result_t; typedef enum efx_phy_cable_status_e { EFX_PHY_CABLE_STATUS_OK, EFX_PHY_CABLE_STATUS_INVALID, EFX_PHY_CABLE_STATUS_OPEN, EFX_PHY_CABLE_STATUS_INTRAPAIRSHORT, EFX_PHY_CABLE_STATUS_INTERPAIRSHORT, EFX_PHY_CABLE_STATUS_BUSY, } efx_phy_cable_status_t; typedef enum efx_bist_value_e { EFX_BIST_PHY_CABLE_LENGTH_A, EFX_BIST_PHY_CABLE_LENGTH_B, EFX_BIST_PHY_CABLE_LENGTH_C, EFX_BIST_PHY_CABLE_LENGTH_D, EFX_BIST_PHY_CABLE_STATUS_A, EFX_BIST_PHY_CABLE_STATUS_B, EFX_BIST_PHY_CABLE_STATUS_C, EFX_BIST_PHY_CABLE_STATUS_D, EFX_BIST_FAULT_CODE, /* * Memory BIST specific values. These match to the MC_CMD_BIST_POLL * response. */ EFX_BIST_MEM_TEST, EFX_BIST_MEM_ADDR, EFX_BIST_MEM_BUS, EFX_BIST_MEM_EXPECT, EFX_BIST_MEM_ACTUAL, EFX_BIST_MEM_ECC, EFX_BIST_MEM_ECC_PARITY, EFX_BIST_MEM_ECC_FATAL, EFX_BIST_NVALUES, } efx_bist_value_t; extern __checkReturn efx_rc_t efx_bist_enable_offline( __in efx_nic_t *enp); extern __checkReturn efx_rc_t efx_bist_start( __in efx_nic_t *enp, __in efx_bist_type_t type); extern __checkReturn efx_rc_t efx_bist_poll( __in efx_nic_t *enp, __in efx_bist_type_t type, __out efx_bist_result_t *resultp, __out_opt uint32_t *value_maskp, __out_ecount_opt(count) unsigned long *valuesp, __in size_t count); extern void efx_bist_stop( __in efx_nic_t *enp, __in efx_bist_type_t type); #endif /* EFSYS_OPT_BIST */ #define EFX_FEATURE_IPV6 0x00000001 #define EFX_FEATURE_LFSR_HASH_INSERT 0x00000002 #define EFX_FEATURE_LINK_EVENTS 0x00000004 #define EFX_FEATURE_PERIODIC_MAC_STATS 0x00000008 #define EFX_FEATURE_MCDI 0x00000020 #define EFX_FEATURE_LOOKAHEAD_SPLIT 0x00000040 #define EFX_FEATURE_MAC_HEADER_FILTERS 0x00000080 #define EFX_FEATURE_TURBO 0x00000100 #define EFX_FEATURE_MCDI_DMA 0x00000200 #define EFX_FEATURE_TX_SRC_FILTERS 0x00000400 #define EFX_FEATURE_PIO_BUFFERS 0x00000800 #define EFX_FEATURE_FW_ASSISTED_TSO 0x00001000 #define EFX_FEATURE_FW_ASSISTED_TSO_V2 0x00002000 #define EFX_FEATURE_PACKED_STREAM 0x00004000 +#define EFX_FEATURE_TXQ_CKSUM_OP_DESC 0x00008000 typedef enum efx_tunnel_protocol_e { EFX_TUNNEL_PROTOCOL_NONE = 0, EFX_TUNNEL_PROTOCOL_VXLAN, EFX_TUNNEL_PROTOCOL_GENEVE, EFX_TUNNEL_PROTOCOL_NVGRE, EFX_TUNNEL_NPROTOS } efx_tunnel_protocol_t; typedef enum efx_vi_window_shift_e { EFX_VI_WINDOW_SHIFT_INVALID = 0, EFX_VI_WINDOW_SHIFT_8K = 13, EFX_VI_WINDOW_SHIFT_16K = 14, EFX_VI_WINDOW_SHIFT_64K = 16, } efx_vi_window_shift_t; typedef struct efx_nic_cfg_s { uint32_t enc_board_type; uint32_t enc_phy_type; #if EFSYS_OPT_NAMES char enc_phy_name[21]; #endif char enc_phy_revision[21]; efx_mon_type_t enc_mon_type; #if EFSYS_OPT_MON_STATS uint32_t enc_mon_stat_dma_buf_size; uint32_t enc_mon_stat_mask[(EFX_MON_NSTATS + 31) / 32]; #endif unsigned int enc_features; efx_vi_window_shift_t enc_vi_window_shift; uint8_t enc_mac_addr[6]; uint8_t enc_port; /* PHY port number */ uint32_t enc_intr_vec_base; uint32_t enc_intr_limit; uint32_t enc_evq_limit; uint32_t enc_txq_limit; uint32_t enc_rxq_limit; uint32_t enc_txq_max_ndescs; uint32_t enc_buftbl_limit; uint32_t enc_piobuf_limit; uint32_t enc_piobuf_size; uint32_t enc_piobuf_min_alloc_size; uint32_t enc_evq_timer_quantum_ns; uint32_t enc_evq_timer_max_us; uint32_t enc_clk_mult; uint32_t enc_rx_prefix_size; uint32_t enc_rx_buf_align_start; uint32_t enc_rx_buf_align_end; #if EFSYS_OPT_RX_SCALE uint32_t enc_rx_scale_max_exclusive_contexts; /* * Mask of supported hash algorithms. * Hash algorithm types are used as the bit indices. */ uint32_t enc_rx_scale_hash_alg_mask; /* * Indicates whether port numbers can be included to the * input data for hash computation. */ boolean_t enc_rx_scale_l4_hash_supported; boolean_t enc_rx_scale_additional_modes_supported; #endif /* EFSYS_OPT_RX_SCALE */ #if EFSYS_OPT_LOOPBACK efx_qword_t enc_loopback_types[EFX_LINK_NMODES]; #endif /* EFSYS_OPT_LOOPBACK */ #if EFSYS_OPT_PHY_FLAGS uint32_t enc_phy_flags_mask; #endif /* EFSYS_OPT_PHY_FLAGS */ #if EFSYS_OPT_PHY_LED_CONTROL uint32_t enc_led_mask; #endif /* EFSYS_OPT_PHY_LED_CONTROL */ #if EFSYS_OPT_PHY_STATS uint64_t enc_phy_stat_mask; #endif /* EFSYS_OPT_PHY_STATS */ #if EFSYS_OPT_MCDI uint8_t enc_mcdi_mdio_channel; #if EFSYS_OPT_PHY_STATS uint32_t enc_mcdi_phy_stat_mask; #endif /* EFSYS_OPT_PHY_STATS */ #if EFSYS_OPT_MON_STATS uint32_t *enc_mcdi_sensor_maskp; uint32_t enc_mcdi_sensor_mask_size; #endif /* EFSYS_OPT_MON_STATS */ #endif /* EFSYS_OPT_MCDI */ #if EFSYS_OPT_BIST uint32_t enc_bist_mask; #endif /* EFSYS_OPT_BIST */ #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 uint32_t enc_pf; uint32_t enc_vf; uint32_t enc_privilege_mask; #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */ boolean_t enc_bug26807_workaround; boolean_t enc_bug35388_workaround; boolean_t enc_bug41750_workaround; boolean_t enc_bug61265_workaround; boolean_t enc_bug61297_workaround; boolean_t enc_rx_batching_enabled; /* Maximum number of descriptors completed in an rx event. */ uint32_t enc_rx_batch_max; /* Number of rx descriptors the hardware requires for a push. */ uint32_t enc_rx_push_align; /* Maximum amount of data in DMA descriptor */ uint32_t enc_tx_dma_desc_size_max; /* * Boundary which DMA descriptor data must not cross or 0 if no * limitation. */ uint32_t enc_tx_dma_desc_boundary; /* * Maximum number of bytes into the packet the TCP header can start for * the hardware to apply TSO packet edits. */ uint32_t enc_tx_tso_tcp_header_offset_limit; boolean_t enc_fw_assisted_tso_enabled; boolean_t enc_fw_assisted_tso_v2_enabled; boolean_t enc_fw_assisted_tso_v2_encap_enabled; /* Number of TSO contexts on the NIC (FATSOv2) */ uint32_t enc_fw_assisted_tso_v2_n_contexts; boolean_t enc_hw_tx_insert_vlan_enabled; /* Number of PFs on the NIC */ uint32_t enc_hw_pf_count; /* Datapath firmware vadapter/vport/vswitch support */ boolean_t enc_datapath_cap_evb; boolean_t enc_rx_disable_scatter_supported; boolean_t enc_allow_set_mac_with_installed_filters; boolean_t enc_enhanced_set_mac_supported; boolean_t enc_init_evq_v2_supported; boolean_t enc_rx_packed_stream_supported; boolean_t enc_rx_var_packed_stream_supported; boolean_t enc_rx_es_super_buffer_supported; boolean_t enc_fw_subvariant_no_tx_csum_supported; boolean_t enc_pm_and_rxdp_counters; boolean_t enc_mac_stats_40g_tx_size_bins; uint32_t enc_tunnel_encapsulations_supported; /* * NIC global maximum for unique UDP tunnel ports shared by all * functions. */ uint32_t enc_tunnel_config_udp_entries_max; /* External port identifier */ uint8_t enc_external_port; uint32_t enc_mcdi_max_payload_length; /* VPD may be per-PF or global */ boolean_t enc_vpd_is_global; /* Minimum unidirectional bandwidth in Mb/s to max out all ports */ uint32_t enc_required_pcie_bandwidth_mbps; uint32_t enc_max_pcie_link_gen; /* Firmware verifies integrity of NVRAM updates */ uint32_t enc_nvram_update_verify_result_supported; /* Firmware support for extended MAC_STATS buffer */ uint32_t enc_mac_stats_nstats; boolean_t enc_fec_counters; boolean_t enc_hlb_counters; /* Firmware support for "FLAG" and "MARK" filter actions */ boolean_t enc_filter_action_flag_supported; boolean_t enc_filter_action_mark_supported; uint32_t enc_filter_action_mark_max; } efx_nic_cfg_t; #define EFX_PCI_FUNCTION_IS_PF(_encp) ((_encp)->enc_vf == 0xffff) #define EFX_PCI_FUNCTION_IS_VF(_encp) ((_encp)->enc_vf != 0xffff) #define EFX_PCI_FUNCTION(_encp) \ (EFX_PCI_FUNCTION_IS_PF(_encp) ? (_encp)->enc_pf : (_encp)->enc_vf) #define EFX_PCI_VF_PARENT(_encp) ((_encp)->enc_pf) extern const efx_nic_cfg_t * efx_nic_cfg_get( __in efx_nic_t *enp); /* RxDPCPU firmware id values by which FW variant can be identified */ #define EFX_RXDP_FULL_FEATURED_FW_ID 0x0 #define EFX_RXDP_LOW_LATENCY_FW_ID 0x1 #define EFX_RXDP_PACKED_STREAM_FW_ID 0x2 #define EFX_RXDP_RULES_ENGINE_FW_ID 0x5 #define EFX_RXDP_DPDK_FW_ID 0x6 typedef struct efx_nic_fw_info_s { /* Basic FW version information */ uint16_t enfi_mc_fw_version[4]; /* * If datapath capabilities can be detected, * additional FW information is to be shown */ boolean_t enfi_dpcpu_fw_ids_valid; /* Rx and Tx datapath CPU FW IDs */ uint16_t enfi_rx_dpcpu_fw_id; uint16_t enfi_tx_dpcpu_fw_id; } efx_nic_fw_info_t; extern __checkReturn efx_rc_t efx_nic_get_fw_version( __in efx_nic_t *enp, __out efx_nic_fw_info_t *enfip); /* Driver resource limits (minimum required/maximum usable). */ typedef struct efx_drv_limits_s { uint32_t edl_min_evq_count; uint32_t edl_max_evq_count; uint32_t edl_min_rxq_count; uint32_t edl_max_rxq_count; uint32_t edl_min_txq_count; uint32_t edl_max_txq_count; /* PIO blocks (sub-allocated from piobuf) */ uint32_t edl_min_pio_alloc_size; uint32_t edl_max_pio_alloc_count; } efx_drv_limits_t; extern __checkReturn efx_rc_t efx_nic_set_drv_limits( __inout efx_nic_t *enp, __in efx_drv_limits_t *edlp); typedef enum efx_nic_region_e { EFX_REGION_VI, /* Memory BAR UC mapping */ EFX_REGION_PIO_WRITE_VI, /* Memory BAR WC mapping */ } efx_nic_region_t; extern __checkReturn efx_rc_t efx_nic_get_bar_region( __in efx_nic_t *enp, __in efx_nic_region_t region, __out uint32_t *offsetp, __out size_t *sizep); extern __checkReturn efx_rc_t efx_nic_get_vi_pool( __in efx_nic_t *enp, __out uint32_t *evq_countp, __out uint32_t *rxq_countp, __out uint32_t *txq_countp); #if EFSYS_OPT_VPD typedef enum efx_vpd_tag_e { EFX_VPD_ID = 0x02, EFX_VPD_END = 0x0f, EFX_VPD_RO = 0x10, EFX_VPD_RW = 0x11, } efx_vpd_tag_t; typedef uint16_t efx_vpd_keyword_t; typedef struct efx_vpd_value_s { efx_vpd_tag_t evv_tag; efx_vpd_keyword_t evv_keyword; uint8_t evv_length; uint8_t evv_value[0x100]; } efx_vpd_value_t; #define EFX_VPD_KEYWORD(x, y) ((x) | ((y) << 8)) extern __checkReturn efx_rc_t efx_vpd_init( __in efx_nic_t *enp); extern __checkReturn efx_rc_t efx_vpd_size( __in efx_nic_t *enp, __out size_t *sizep); extern __checkReturn efx_rc_t efx_vpd_read( __in efx_nic_t *enp, __out_bcount(size) caddr_t data, __in size_t size); extern __checkReturn efx_rc_t efx_vpd_verify( __in efx_nic_t *enp, __in_bcount(size) caddr_t data, __in size_t size); extern __checkReturn efx_rc_t efx_vpd_reinit( __in efx_nic_t *enp, __in_bcount(size) caddr_t data, __in size_t size); extern __checkReturn efx_rc_t efx_vpd_get( __in efx_nic_t *enp, __in_bcount(size) caddr_t data, __in size_t size, __inout efx_vpd_value_t *evvp); extern __checkReturn efx_rc_t efx_vpd_set( __in efx_nic_t *enp, __inout_bcount(size) caddr_t data, __in size_t size, __in efx_vpd_value_t *evvp); extern __checkReturn efx_rc_t efx_vpd_next( __in efx_nic_t *enp, __inout_bcount(size) caddr_t data, __in size_t size, __out efx_vpd_value_t *evvp, __inout unsigned int *contp); extern __checkReturn efx_rc_t efx_vpd_write( __in efx_nic_t *enp, __in_bcount(size) caddr_t data, __in size_t size); extern void efx_vpd_fini( __in efx_nic_t *enp); #endif /* EFSYS_OPT_VPD */ /* NVRAM */ #if EFSYS_OPT_NVRAM typedef enum efx_nvram_type_e { EFX_NVRAM_INVALID = 0, EFX_NVRAM_BOOTROM, EFX_NVRAM_BOOTROM_CFG, EFX_NVRAM_MC_FIRMWARE, EFX_NVRAM_MC_GOLDEN, EFX_NVRAM_PHY, EFX_NVRAM_NULLPHY, EFX_NVRAM_FPGA, EFX_NVRAM_FCFW, EFX_NVRAM_CPLD, EFX_NVRAM_FPGA_BACKUP, EFX_NVRAM_DYNAMIC_CFG, EFX_NVRAM_LICENSE, EFX_NVRAM_UEFIROM, EFX_NVRAM_MUM_FIRMWARE, EFX_NVRAM_DYNCONFIG_DEFAULTS, EFX_NVRAM_ROMCONFIG_DEFAULTS, EFX_NVRAM_NTYPES, } efx_nvram_type_t; extern __checkReturn efx_rc_t efx_nvram_init( __in efx_nic_t *enp); #if EFSYS_OPT_DIAG extern __checkReturn efx_rc_t efx_nvram_test( __in efx_nic_t *enp); #endif /* EFSYS_OPT_DIAG */ extern __checkReturn efx_rc_t efx_nvram_size( __in efx_nic_t *enp, __in efx_nvram_type_t type, __out size_t *sizep); extern __checkReturn efx_rc_t efx_nvram_rw_start( __in efx_nic_t *enp, __in efx_nvram_type_t type, __out_opt size_t *pref_chunkp); extern __checkReturn efx_rc_t efx_nvram_rw_finish( __in efx_nic_t *enp, __in efx_nvram_type_t type, __out_opt uint32_t *verify_resultp); extern __checkReturn efx_rc_t efx_nvram_get_version( __in efx_nic_t *enp, __in efx_nvram_type_t type, __out uint32_t *subtypep, __out_ecount(4) uint16_t version[4]); extern __checkReturn efx_rc_t efx_nvram_read_chunk( __in efx_nic_t *enp, __in efx_nvram_type_t type, __in unsigned int offset, __out_bcount(size) caddr_t data, __in size_t size); extern __checkReturn efx_rc_t efx_nvram_read_backup( __in efx_nic_t *enp, __in efx_nvram_type_t type, __in unsigned int offset, __out_bcount(size) caddr_t data, __in size_t size); extern __checkReturn efx_rc_t efx_nvram_set_version( __in efx_nic_t *enp, __in efx_nvram_type_t type, __in_ecount(4) uint16_t version[4]); extern __checkReturn efx_rc_t efx_nvram_validate( __in efx_nic_t *enp, __in efx_nvram_type_t type, __in_bcount(partn_size) caddr_t partn_data, __in size_t partn_size); extern __checkReturn efx_rc_t efx_nvram_erase( __in efx_nic_t *enp, __in efx_nvram_type_t type); extern __checkReturn efx_rc_t efx_nvram_write_chunk( __in efx_nic_t *enp, __in efx_nvram_type_t type, __in unsigned int offset, __in_bcount(size) caddr_t data, __in size_t size); extern void efx_nvram_fini( __in efx_nic_t *enp); #endif /* EFSYS_OPT_NVRAM */ #if EFSYS_OPT_BOOTCFG /* Report size and offset of bootcfg sector in NVRAM partition. */ extern __checkReturn efx_rc_t efx_bootcfg_sector_info( __in efx_nic_t *enp, __in uint32_t pf, __out_opt uint32_t *sector_countp, __out size_t *offsetp, __out size_t *max_sizep); /* * Copy bootcfg sector data to a target buffer which may differ in size. * Optionally corrects format errors in source buffer. */ extern efx_rc_t efx_bootcfg_copy_sector( __in efx_nic_t *enp, __inout_bcount(sector_length) uint8_t *sector, __in size_t sector_length, __out_bcount(data_size) uint8_t *data, __in size_t data_size, __in boolean_t handle_format_errors); extern efx_rc_t efx_bootcfg_read( __in efx_nic_t *enp, __out_bcount(size) uint8_t *data, __in size_t size); extern efx_rc_t efx_bootcfg_write( __in efx_nic_t *enp, __in_bcount(size) uint8_t *data, __in size_t size); /* * Processing routines for buffers arranged in the DHCP/BOOTP option format * (see https://tools.ietf.org/html/rfc1533) * * Summarising the format: the buffer is a sequence of options. All options * begin with a tag octet, which uniquely identifies the option. Fixed- * length options without data consist of only a tag octet. Only options PAD * (0) and END (255) are fixed length. All other options are variable-length * with a length octet following the tag octet. The value of the length * octet does not include the two octets specifying the tag and length. The * length octet is followed by "length" octets of data. * * Option data may be a sequence of sub-options in the same format. The data * content of the encapsulating option is one or more encapsulated sub-options, * with no terminating END tag is required. * * To be valid, the top-level sequence of options should be terminated by an * END tag. The buffer should be padded with the PAD byte. * * When stored to NVRAM, the DHCP option format buffer is preceded by a * checksum octet. The full buffer (including after the END tag) contributes * to the checksum, hence the need to fill the buffer to the end with PAD. */ #define EFX_DHCP_END ((uint8_t)0xff) #define EFX_DHCP_PAD ((uint8_t)0) #define EFX_DHCP_ENCAP_OPT(encapsulator, encapsulated) \ (uint16_t)(((encapsulator) << 8) | (encapsulated)) extern __checkReturn uint8_t efx_dhcp_csum( __in_bcount(size) uint8_t const *data, __in size_t size); extern __checkReturn efx_rc_t efx_dhcp_verify( __in_bcount(size) uint8_t const *data, __in size_t size, __out_opt size_t *usedp); extern __checkReturn efx_rc_t efx_dhcp_find_tag( __in_bcount(buffer_length) uint8_t *bufferp, __in size_t buffer_length, __in uint16_t opt, __deref_out uint8_t **valuepp, __out size_t *value_lengthp); extern __checkReturn efx_rc_t efx_dhcp_find_end( __in_bcount(buffer_length) uint8_t *bufferp, __in size_t buffer_length, __deref_out uint8_t **endpp); extern __checkReturn efx_rc_t efx_dhcp_delete_tag( __inout_bcount(buffer_length) uint8_t *bufferp, __in size_t buffer_length, __in uint16_t opt); extern __checkReturn efx_rc_t efx_dhcp_add_tag( __inout_bcount(buffer_length) uint8_t *bufferp, __in size_t buffer_length, __in uint16_t opt, __in_bcount_opt(value_length) uint8_t *valuep, __in size_t value_length); extern __checkReturn efx_rc_t efx_dhcp_update_tag( __inout_bcount(buffer_length) uint8_t *bufferp, __in size_t buffer_length, __in uint16_t opt, __in uint8_t *value_locationp, __in_bcount_opt(value_length) uint8_t *valuep, __in size_t value_length); #endif /* EFSYS_OPT_BOOTCFG */ #if EFSYS_OPT_IMAGE_LAYOUT #include "ef10_signed_image_layout.h" /* * Image header used in unsigned and signed image layouts (see SF-102785-PS). * * NOTE: * The image header format is extensible. However, older drivers require an * exact match of image header version and header length when validating and * writing firmware images. * * To avoid breaking backward compatibility, we use the upper bits of the * controller version fields to contain an extra version number used for * combined bootROM and UEFI ROM images on EF10 and later (to hold the UEFI ROM * version). See bug39254 and SF-102785-PS for details. */ typedef struct efx_image_header_s { uint32_t eih_magic; uint32_t eih_version; uint32_t eih_type; uint32_t eih_subtype; uint32_t eih_code_size; uint32_t eih_size; union { uint32_t eih_controller_version_min; struct { uint16_t eih_controller_version_min_short; uint8_t eih_extra_version_a; uint8_t eih_extra_version_b; }; }; union { uint32_t eih_controller_version_max; struct { uint16_t eih_controller_version_max_short; uint8_t eih_extra_version_c; uint8_t eih_extra_version_d; }; }; uint16_t eih_code_version_a; uint16_t eih_code_version_b; uint16_t eih_code_version_c; uint16_t eih_code_version_d; } efx_image_header_t; #define EFX_IMAGE_HEADER_SIZE (40) #define EFX_IMAGE_HEADER_VERSION (4) #define EFX_IMAGE_HEADER_MAGIC (0x106F1A5) typedef struct efx_image_trailer_s { uint32_t eit_crc; } efx_image_trailer_t; #define EFX_IMAGE_TRAILER_SIZE (4) typedef enum efx_image_format_e { EFX_IMAGE_FORMAT_NO_IMAGE, EFX_IMAGE_FORMAT_INVALID, EFX_IMAGE_FORMAT_UNSIGNED, EFX_IMAGE_FORMAT_SIGNED, } efx_image_format_t; typedef struct efx_image_info_s { efx_image_format_t eii_format; uint8_t * eii_imagep; size_t eii_image_size; efx_image_header_t * eii_headerp; } efx_image_info_t; extern __checkReturn efx_rc_t efx_check_reflash_image( __in void *bufferp, __in uint32_t buffer_size, __out efx_image_info_t *infop); extern __checkReturn efx_rc_t efx_build_signed_image_write_buffer( __out_bcount(buffer_size) uint8_t *bufferp, __in uint32_t buffer_size, __in efx_image_info_t *infop, __out efx_image_header_t **headerpp); #endif /* EFSYS_OPT_IMAGE_LAYOUT */ #if EFSYS_OPT_DIAG typedef enum efx_pattern_type_t { EFX_PATTERN_BYTE_INCREMENT = 0, EFX_PATTERN_ALL_THE_SAME, EFX_PATTERN_BIT_ALTERNATE, EFX_PATTERN_BYTE_ALTERNATE, EFX_PATTERN_BYTE_CHANGING, EFX_PATTERN_BIT_SWEEP, EFX_PATTERN_NTYPES } efx_pattern_type_t; typedef void (*efx_sram_pattern_fn_t)( __in size_t row, __in boolean_t negate, __out efx_qword_t *eqp); extern __checkReturn efx_rc_t efx_sram_test( __in efx_nic_t *enp, __in efx_pattern_type_t type); #endif /* EFSYS_OPT_DIAG */ extern __checkReturn efx_rc_t efx_sram_buf_tbl_set( __in efx_nic_t *enp, __in uint32_t id, __in efsys_mem_t *esmp, __in size_t n); extern void efx_sram_buf_tbl_clear( __in efx_nic_t *enp, __in uint32_t id, __in size_t n); #define EFX_BUF_TBL_SIZE 0x20000 #define EFX_BUF_SIZE 4096 /* EV */ typedef struct efx_evq_s efx_evq_t; #if EFSYS_OPT_QSTATS /* START MKCONFIG GENERATED EfxHeaderEventQueueBlock 6f3843f5fe7cc843 */ typedef enum efx_ev_qstat_e { EV_ALL, EV_RX, EV_RX_OK, EV_RX_FRM_TRUNC, EV_RX_TOBE_DISC, EV_RX_PAUSE_FRM_ERR, EV_RX_BUF_OWNER_ID_ERR, EV_RX_IPV4_HDR_CHKSUM_ERR, EV_RX_TCP_UDP_CHKSUM_ERR, EV_RX_ETH_CRC_ERR, EV_RX_IP_FRAG_ERR, EV_RX_MCAST_PKT, EV_RX_MCAST_HASH_MATCH, EV_RX_TCP_IPV4, EV_RX_TCP_IPV6, EV_RX_UDP_IPV4, EV_RX_UDP_IPV6, EV_RX_OTHER_IPV4, EV_RX_OTHER_IPV6, EV_RX_NON_IP, EV_RX_BATCH, EV_TX, EV_TX_WQ_FF_FULL, EV_TX_PKT_ERR, EV_TX_PKT_TOO_BIG, EV_TX_UNEXPECTED, EV_GLOBAL, EV_GLOBAL_MNT, EV_DRIVER, EV_DRIVER_SRM_UPD_DONE, EV_DRIVER_TX_DESCQ_FLS_DONE, EV_DRIVER_RX_DESCQ_FLS_DONE, EV_DRIVER_RX_DESCQ_FLS_FAILED, EV_DRIVER_RX_DSC_ERROR, EV_DRIVER_TX_DSC_ERROR, EV_DRV_GEN, EV_MCDI_RESPONSE, EV_NQSTATS } efx_ev_qstat_t; /* END MKCONFIG GENERATED EfxHeaderEventQueueBlock */ #endif /* EFSYS_OPT_QSTATS */ extern __checkReturn efx_rc_t efx_ev_init( __in efx_nic_t *enp); extern void efx_ev_fini( __in efx_nic_t *enp); #define EFX_EVQ_MAXNEVS 32768 #define EFX_EVQ_MINNEVS 512 #define EFX_EVQ_SIZE(_nevs) ((_nevs) * sizeof (efx_qword_t)) #define EFX_EVQ_NBUFS(_nevs) (EFX_EVQ_SIZE(_nevs) / EFX_BUF_SIZE) #define EFX_EVQ_FLAGS_TYPE_MASK (0x3) #define EFX_EVQ_FLAGS_TYPE_AUTO (0x0) #define EFX_EVQ_FLAGS_TYPE_THROUGHPUT (0x1) #define EFX_EVQ_FLAGS_TYPE_LOW_LATENCY (0x2) #define EFX_EVQ_FLAGS_NOTIFY_MASK (0xC) #define EFX_EVQ_FLAGS_NOTIFY_INTERRUPT (0x0) /* Interrupting (default) */ #define EFX_EVQ_FLAGS_NOTIFY_DISABLED (0x4) /* Non-interrupting */ extern __checkReturn efx_rc_t efx_ev_qcreate( __in efx_nic_t *enp, __in unsigned int index, __in efsys_mem_t *esmp, __in size_t ndescs, __in uint32_t id, __in uint32_t us, __in uint32_t flags, __deref_out efx_evq_t **eepp); extern void efx_ev_qpost( __in efx_evq_t *eep, __in uint16_t data); typedef __checkReturn boolean_t (*efx_initialized_ev_t)( __in_opt void *arg); #define EFX_PKT_UNICAST 0x0004 #define EFX_PKT_START 0x0008 #define EFX_PKT_VLAN_TAGGED 0x0010 #define EFX_CKSUM_TCPUDP 0x0020 #define EFX_CKSUM_IPV4 0x0040 #define EFX_PKT_CONT 0x0080 #define EFX_CHECK_VLAN 0x0100 #define EFX_PKT_TCP 0x0200 #define EFX_PKT_UDP 0x0400 #define EFX_PKT_IPV4 0x0800 #define EFX_PKT_IPV6 0x1000 #define EFX_PKT_PREFIX_LEN 0x2000 #define EFX_ADDR_MISMATCH 0x4000 #define EFX_DISCARD 0x8000 /* * The following flags are used only for packed stream * mode. The values for the flags are reused to fit into 16 bit, * since EFX_PKT_START and EFX_PKT_CONT are never used in * packed stream mode */ #define EFX_PKT_PACKED_STREAM_NEW_BUFFER EFX_PKT_START #define EFX_PKT_PACKED_STREAM_PARSE_INCOMPLETE EFX_PKT_CONT #define EFX_EV_RX_NLABELS 32 #define EFX_EV_TX_NLABELS 32 typedef __checkReturn boolean_t (*efx_rx_ev_t)( __in_opt void *arg, __in uint32_t label, __in uint32_t id, __in uint32_t size, __in uint16_t flags); #if EFSYS_OPT_RX_PACKED_STREAM || EFSYS_OPT_RX_ES_SUPER_BUFFER /* * Packed stream mode is documented in SF-112241-TC. * The general idea is that, instead of putting each incoming * packet into a separate buffer which is specified in a RX * descriptor, a large buffer is provided to the hardware and * packets are put there in a continuous stream. * The main advantage of such an approach is that RX queue refilling * happens much less frequently. * * Equal stride packed stream mode is documented in SF-119419-TC. * The general idea is to utilize advantages of the packed stream, * but avoid indirection in packets representation. * The main advantage of such an approach is that RX queue refilling * happens much less frequently and packets buffers are independent * from upper layers point of view. */ typedef __checkReturn boolean_t (*efx_rx_ps_ev_t)( __in_opt void *arg, __in uint32_t label, __in uint32_t id, __in uint32_t pkt_count, __in uint16_t flags); #endif typedef __checkReturn boolean_t (*efx_tx_ev_t)( __in_opt void *arg, __in uint32_t label, __in uint32_t id); #define EFX_EXCEPTION_RX_RECOVERY 0x00000001 #define EFX_EXCEPTION_RX_DSC_ERROR 0x00000002 #define EFX_EXCEPTION_TX_DSC_ERROR 0x00000003 #define EFX_EXCEPTION_UNKNOWN_SENSOREVT 0x00000004 #define EFX_EXCEPTION_FWALERT_SRAM 0x00000005 #define EFX_EXCEPTION_UNKNOWN_FWALERT 0x00000006 #define EFX_EXCEPTION_RX_ERROR 0x00000007 #define EFX_EXCEPTION_TX_ERROR 0x00000008 #define EFX_EXCEPTION_EV_ERROR 0x00000009 typedef __checkReturn boolean_t (*efx_exception_ev_t)( __in_opt void *arg, __in uint32_t label, __in uint32_t data); typedef __checkReturn boolean_t (*efx_rxq_flush_done_ev_t)( __in_opt void *arg, __in uint32_t rxq_index); typedef __checkReturn boolean_t (*efx_rxq_flush_failed_ev_t)( __in_opt void *arg, __in uint32_t rxq_index); typedef __checkReturn boolean_t (*efx_txq_flush_done_ev_t)( __in_opt void *arg, __in uint32_t txq_index); typedef __checkReturn boolean_t (*efx_software_ev_t)( __in_opt void *arg, __in uint16_t magic); typedef __checkReturn boolean_t (*efx_sram_ev_t)( __in_opt void *arg, __in uint32_t code); #define EFX_SRAM_CLEAR 0 #define EFX_SRAM_UPDATE 1 #define EFX_SRAM_ILLEGAL_CLEAR 2 typedef __checkReturn boolean_t (*efx_wake_up_ev_t)( __in_opt void *arg, __in uint32_t label); typedef __checkReturn boolean_t (*efx_timer_ev_t)( __in_opt void *arg, __in uint32_t label); typedef __checkReturn boolean_t (*efx_link_change_ev_t)( __in_opt void *arg, __in efx_link_mode_t link_mode); #if EFSYS_OPT_MON_STATS typedef __checkReturn boolean_t (*efx_monitor_ev_t)( __in_opt void *arg, __in efx_mon_stat_t id, __in efx_mon_stat_value_t value); #endif /* EFSYS_OPT_MON_STATS */ #if EFSYS_OPT_MAC_STATS typedef __checkReturn boolean_t (*efx_mac_stats_ev_t)( __in_opt void *arg, __in uint32_t generation); #endif /* EFSYS_OPT_MAC_STATS */ typedef struct efx_ev_callbacks_s { efx_initialized_ev_t eec_initialized; efx_rx_ev_t eec_rx; #if EFSYS_OPT_RX_PACKED_STREAM || EFSYS_OPT_RX_ES_SUPER_BUFFER efx_rx_ps_ev_t eec_rx_ps; #endif efx_tx_ev_t eec_tx; efx_exception_ev_t eec_exception; efx_rxq_flush_done_ev_t eec_rxq_flush_done; efx_rxq_flush_failed_ev_t eec_rxq_flush_failed; efx_txq_flush_done_ev_t eec_txq_flush_done; efx_software_ev_t eec_software; efx_sram_ev_t eec_sram; efx_wake_up_ev_t eec_wake_up; efx_timer_ev_t eec_timer; efx_link_change_ev_t eec_link_change; #if EFSYS_OPT_MON_STATS efx_monitor_ev_t eec_monitor; #endif /* EFSYS_OPT_MON_STATS */ #if EFSYS_OPT_MAC_STATS efx_mac_stats_ev_t eec_mac_stats; #endif /* EFSYS_OPT_MAC_STATS */ } efx_ev_callbacks_t; extern __checkReturn boolean_t efx_ev_qpending( __in efx_evq_t *eep, __in unsigned int count); #if EFSYS_OPT_EV_PREFETCH extern void efx_ev_qprefetch( __in efx_evq_t *eep, __in unsigned int count); #endif /* EFSYS_OPT_EV_PREFETCH */ extern void efx_ev_qpoll( __in efx_evq_t *eep, __inout unsigned int *countp, __in const efx_ev_callbacks_t *eecp, __in_opt void *arg); extern __checkReturn efx_rc_t efx_ev_usecs_to_ticks( __in efx_nic_t *enp, __in unsigned int usecs, __out unsigned int *ticksp); extern __checkReturn efx_rc_t efx_ev_qmoderate( __in efx_evq_t *eep, __in unsigned int us); extern __checkReturn efx_rc_t efx_ev_qprime( __in efx_evq_t *eep, __in unsigned int count); #if EFSYS_OPT_QSTATS #if EFSYS_OPT_NAMES extern const char * efx_ev_qstat_name( __in efx_nic_t *enp, __in unsigned int id); #endif /* EFSYS_OPT_NAMES */ extern void efx_ev_qstats_update( __in efx_evq_t *eep, __inout_ecount(EV_NQSTATS) efsys_stat_t *stat); #endif /* EFSYS_OPT_QSTATS */ extern void efx_ev_qdestroy( __in efx_evq_t *eep); /* RX */ extern __checkReturn efx_rc_t efx_rx_init( __inout efx_nic_t *enp); extern void efx_rx_fini( __in efx_nic_t *enp); #if EFSYS_OPT_RX_SCATTER __checkReturn efx_rc_t efx_rx_scatter_enable( __in efx_nic_t *enp, __in unsigned int buf_size); #endif /* EFSYS_OPT_RX_SCATTER */ /* Handle to represent use of the default RSS context. */ #define EFX_RSS_CONTEXT_DEFAULT 0xffffffff #if EFSYS_OPT_RX_SCALE typedef enum efx_rx_hash_alg_e { EFX_RX_HASHALG_LFSR = 0, EFX_RX_HASHALG_TOEPLITZ, EFX_RX_HASHALG_PACKED_STREAM, EFX_RX_NHASHALGS } efx_rx_hash_alg_t; /* * Legacy hash type flags. * * They represent standard tuples for distinct traffic classes. */ #define EFX_RX_HASH_IPV4 (1U << 0) #define EFX_RX_HASH_TCPIPV4 (1U << 1) #define EFX_RX_HASH_IPV6 (1U << 2) #define EFX_RX_HASH_TCPIPV6 (1U << 3) #define EFX_RX_HASH_LEGACY_MASK \ (EFX_RX_HASH_IPV4 | \ EFX_RX_HASH_TCPIPV4 | \ EFX_RX_HASH_IPV6 | \ EFX_RX_HASH_TCPIPV6) /* * The type of the argument used by efx_rx_scale_mode_set() to * provide a means for the client drivers to configure hashing. * * A properly constructed value can either be: * - a combination of legacy flags * - a combination of EFX_RX_HASH() flags */ typedef uint32_t efx_rx_hash_type_t; typedef enum efx_rx_hash_support_e { EFX_RX_HASH_UNAVAILABLE = 0, /* Hardware hash not inserted */ EFX_RX_HASH_AVAILABLE /* Insert hash with/without RSS */ } efx_rx_hash_support_t; #define EFX_RSS_KEY_SIZE 40 /* RSS key size (bytes) */ #define EFX_RSS_TBL_SIZE 128 /* Rows in RX indirection table */ #define EFX_MAXRSS 64 /* RX indirection entry range */ #define EFX_MAXRSS_LEGACY 16 /* See bug16611 and bug17213 */ typedef enum efx_rx_scale_context_type_e { EFX_RX_SCALE_UNAVAILABLE = 0, /* No RX scale context */ EFX_RX_SCALE_EXCLUSIVE, /* Writable key/indirection table */ EFX_RX_SCALE_SHARED /* Read-only key/indirection table */ } efx_rx_scale_context_type_t; /* * Traffic classes eligible for hash computation. * * Select packet headers used in computing the receive hash. * This uses the same encoding as the RSS_MODES field of * MC_CMD_RSS_CONTEXT_SET_FLAGS. */ #define EFX_RX_CLASS_IPV4_TCP_LBN 8 #define EFX_RX_CLASS_IPV4_TCP_WIDTH 4 #define EFX_RX_CLASS_IPV4_UDP_LBN 12 #define EFX_RX_CLASS_IPV4_UDP_WIDTH 4 #define EFX_RX_CLASS_IPV4_LBN 16 #define EFX_RX_CLASS_IPV4_WIDTH 4 #define EFX_RX_CLASS_IPV6_TCP_LBN 20 #define EFX_RX_CLASS_IPV6_TCP_WIDTH 4 #define EFX_RX_CLASS_IPV6_UDP_LBN 24 #define EFX_RX_CLASS_IPV6_UDP_WIDTH 4 #define EFX_RX_CLASS_IPV6_LBN 28 #define EFX_RX_CLASS_IPV6_WIDTH 4 #define EFX_RX_NCLASSES 6 /* * Ancillary flags used to construct generic hash tuples. * This uses the same encoding as RSS_MODE_HASH_SELECTOR. */ #define EFX_RX_CLASS_HASH_SRC_ADDR (1U << 0) #define EFX_RX_CLASS_HASH_DST_ADDR (1U << 1) #define EFX_RX_CLASS_HASH_SRC_PORT (1U << 2) #define EFX_RX_CLASS_HASH_DST_PORT (1U << 3) /* * Generic hash tuples. * * They express combinations of packet fields * which can contribute to the hash value for * a particular traffic class. */ #define EFX_RX_CLASS_HASH_DISABLE 0 #define EFX_RX_CLASS_HASH_1TUPLE_SRC EFX_RX_CLASS_HASH_SRC_ADDR #define EFX_RX_CLASS_HASH_1TUPLE_DST EFX_RX_CLASS_HASH_DST_ADDR #define EFX_RX_CLASS_HASH_2TUPLE \ (EFX_RX_CLASS_HASH_SRC_ADDR | \ EFX_RX_CLASS_HASH_DST_ADDR) #define EFX_RX_CLASS_HASH_2TUPLE_SRC \ (EFX_RX_CLASS_HASH_SRC_ADDR | \ EFX_RX_CLASS_HASH_SRC_PORT) #define EFX_RX_CLASS_HASH_2TUPLE_DST \ (EFX_RX_CLASS_HASH_DST_ADDR | \ EFX_RX_CLASS_HASH_DST_PORT) #define EFX_RX_CLASS_HASH_4TUPLE \ (EFX_RX_CLASS_HASH_SRC_ADDR | \ EFX_RX_CLASS_HASH_DST_ADDR | \ EFX_RX_CLASS_HASH_SRC_PORT | \ EFX_RX_CLASS_HASH_DST_PORT) #define EFX_RX_CLASS_HASH_NTUPLES 7 /* * Hash flag constructor. * * Resulting flags encode hash tuples for specific traffic classes. * The client drivers are encouraged to use these flags to form * a hash type value. */ #define EFX_RX_HASH(_class, _tuple) \ EFX_INSERT_FIELD_NATIVE32(0, 31, \ EFX_RX_CLASS_##_class, EFX_RX_CLASS_HASH_##_tuple) /* * The maximum number of EFX_RX_HASH() flags. */ #define EFX_RX_HASH_NFLAGS (EFX_RX_NCLASSES * EFX_RX_CLASS_HASH_NTUPLES) extern __checkReturn efx_rc_t efx_rx_scale_hash_flags_get( __in efx_nic_t *enp, __in efx_rx_hash_alg_t hash_alg, __out_ecount_part(max_nflags, *nflagsp) unsigned int *flagsp, __in unsigned int max_nflags, __out unsigned int *nflagsp); extern __checkReturn efx_rc_t efx_rx_hash_default_support_get( __in efx_nic_t *enp, __out efx_rx_hash_support_t *supportp); extern __checkReturn efx_rc_t efx_rx_scale_default_support_get( __in efx_nic_t *enp, __out efx_rx_scale_context_type_t *typep); extern __checkReturn efx_rc_t efx_rx_scale_context_alloc( __in efx_nic_t *enp, __in efx_rx_scale_context_type_t type, __in uint32_t num_queues, __out uint32_t *rss_contextp); extern __checkReturn efx_rc_t efx_rx_scale_context_free( __in efx_nic_t *enp, __in uint32_t rss_context); extern __checkReturn efx_rc_t efx_rx_scale_mode_set( __in efx_nic_t *enp, __in uint32_t rss_context, __in efx_rx_hash_alg_t alg, __in efx_rx_hash_type_t type, __in boolean_t insert); extern __checkReturn efx_rc_t efx_rx_scale_tbl_set( __in efx_nic_t *enp, __in uint32_t rss_context, __in_ecount(n) unsigned int *table, __in size_t n); extern __checkReturn efx_rc_t efx_rx_scale_key_set( __in efx_nic_t *enp, __in uint32_t rss_context, __in_ecount(n) uint8_t *key, __in size_t n); extern __checkReturn uint32_t efx_pseudo_hdr_hash_get( __in efx_rxq_t *erp, __in efx_rx_hash_alg_t func, __in uint8_t *buffer); #endif /* EFSYS_OPT_RX_SCALE */ extern __checkReturn efx_rc_t efx_pseudo_hdr_pkt_length_get( __in efx_rxq_t *erp, __in uint8_t *buffer, __out uint16_t *pkt_lengthp); #define EFX_RXQ_MAXNDESCS 4096 #define EFX_RXQ_MINNDESCS 512 #define EFX_RXQ_SIZE(_ndescs) ((_ndescs) * sizeof (efx_qword_t)) #define EFX_RXQ_NBUFS(_ndescs) (EFX_RXQ_SIZE(_ndescs) / EFX_BUF_SIZE) #define EFX_RXQ_LIMIT(_ndescs) ((_ndescs) - 16) #define EFX_RXQ_DC_NDESCS(_dcsize) (8 << _dcsize) typedef enum efx_rxq_type_e { EFX_RXQ_TYPE_DEFAULT, EFX_RXQ_TYPE_PACKED_STREAM, EFX_RXQ_TYPE_ES_SUPER_BUFFER, EFX_RXQ_NTYPES } efx_rxq_type_t; /* * Dummy flag to be used instead of 0 to make it clear that the argument * is receive queue flags. */ #define EFX_RXQ_FLAG_NONE 0x0 #define EFX_RXQ_FLAG_SCATTER 0x1 /* * If tunnels are supported and Rx event can provide information about * either outer or inner packet classes (e.g. SFN8xxx adapters with * full-feature firmware variant running), outer classes are requested by * default. However, if the driver supports tunnels, the flag allows to * request inner classes which are required to be able to interpret inner * Rx checksum offload results. */ #define EFX_RXQ_FLAG_INNER_CLASSES 0x2 extern __checkReturn efx_rc_t efx_rx_qcreate( __in efx_nic_t *enp, __in unsigned int index, __in unsigned int label, __in efx_rxq_type_t type, __in efsys_mem_t *esmp, __in size_t ndescs, __in uint32_t id, __in unsigned int flags, __in efx_evq_t *eep, __deref_out efx_rxq_t **erpp); #if EFSYS_OPT_RX_PACKED_STREAM #define EFX_RXQ_PACKED_STREAM_BUF_SIZE_1M (1U * 1024 * 1024) #define EFX_RXQ_PACKED_STREAM_BUF_SIZE_512K (512U * 1024) #define EFX_RXQ_PACKED_STREAM_BUF_SIZE_256K (256U * 1024) #define EFX_RXQ_PACKED_STREAM_BUF_SIZE_128K (128U * 1024) #define EFX_RXQ_PACKED_STREAM_BUF_SIZE_64K (64U * 1024) extern __checkReturn efx_rc_t efx_rx_qcreate_packed_stream( __in efx_nic_t *enp, __in unsigned int index, __in unsigned int label, __in uint32_t ps_buf_size, __in efsys_mem_t *esmp, __in size_t ndescs, __in efx_evq_t *eep, __deref_out efx_rxq_t **erpp); #endif #if EFSYS_OPT_RX_ES_SUPER_BUFFER /* Maximum head-of-line block timeout in nanoseconds */ #define EFX_RXQ_ES_SUPER_BUFFER_HOL_BLOCK_MAX (400U * 1000 * 1000) extern __checkReturn efx_rc_t efx_rx_qcreate_es_super_buffer( __in efx_nic_t *enp, __in unsigned int index, __in unsigned int label, __in uint32_t n_bufs_per_desc, __in uint32_t max_dma_len, __in uint32_t buf_stride, __in uint32_t hol_block_timeout, __in efsys_mem_t *esmp, __in size_t ndescs, __in unsigned int flags, __in efx_evq_t *eep, __deref_out efx_rxq_t **erpp); #endif typedef struct efx_buffer_s { efsys_dma_addr_t eb_addr; size_t eb_size; boolean_t eb_eop; } efx_buffer_t; typedef struct efx_desc_s { efx_qword_t ed_eq; } efx_desc_t; extern void efx_rx_qpost( __in efx_rxq_t *erp, __in_ecount(ndescs) efsys_dma_addr_t *addrp, __in size_t size, __in unsigned int ndescs, __in unsigned int completed, __in unsigned int added); extern void efx_rx_qpush( __in efx_rxq_t *erp, __in unsigned int added, __inout unsigned int *pushedp); #if EFSYS_OPT_RX_PACKED_STREAM extern void efx_rx_qpush_ps_credits( __in efx_rxq_t *erp); extern __checkReturn uint8_t * efx_rx_qps_packet_info( __in efx_rxq_t *erp, __in uint8_t *buffer, __in uint32_t buffer_length, __in uint32_t current_offset, __out uint16_t *lengthp, __out uint32_t *next_offsetp, __out uint32_t *timestamp); #endif extern __checkReturn efx_rc_t efx_rx_qflush( __in efx_rxq_t *erp); extern void efx_rx_qenable( __in efx_rxq_t *erp); extern void efx_rx_qdestroy( __in efx_rxq_t *erp); /* TX */ typedef struct efx_txq_s efx_txq_t; #if EFSYS_OPT_QSTATS /* START MKCONFIG GENERATED EfxHeaderTransmitQueueBlock 12dff8778598b2db */ typedef enum efx_tx_qstat_e { TX_POST, TX_POST_PIO, TX_NQSTATS } efx_tx_qstat_t; /* END MKCONFIG GENERATED EfxHeaderTransmitQueueBlock */ #endif /* EFSYS_OPT_QSTATS */ extern __checkReturn efx_rc_t efx_tx_init( __in efx_nic_t *enp); extern void efx_tx_fini( __in efx_nic_t *enp); #define EFX_TXQ_MINNDESCS 512 #define EFX_TXQ_SIZE(_ndescs) ((_ndescs) * sizeof (efx_qword_t)) #define EFX_TXQ_NBUFS(_ndescs) (EFX_TXQ_SIZE(_ndescs) / EFX_BUF_SIZE) #define EFX_TXQ_LIMIT(_ndescs) ((_ndescs) - 16) #define EFX_TXQ_MAX_BUFS 8 /* Maximum independent of EFX_BUG35388_WORKAROUND. */ #define EFX_TXQ_CKSUM_IPV4 0x0001 #define EFX_TXQ_CKSUM_TCPUDP 0x0002 #define EFX_TXQ_FATSOV2 0x0004 #define EFX_TXQ_CKSUM_INNER_IPV4 0x0008 #define EFX_TXQ_CKSUM_INNER_TCPUDP 0x0010 extern __checkReturn efx_rc_t efx_tx_qcreate( __in efx_nic_t *enp, __in unsigned int index, __in unsigned int label, __in efsys_mem_t *esmp, __in size_t n, __in uint32_t id, __in uint16_t flags, __in efx_evq_t *eep, __deref_out efx_txq_t **etpp, __out unsigned int *addedp); extern __checkReturn efx_rc_t efx_tx_qpost( __in efx_txq_t *etp, __in_ecount(ndescs) efx_buffer_t *eb, __in unsigned int ndescs, __in unsigned int completed, __inout unsigned int *addedp); extern __checkReturn efx_rc_t efx_tx_qpace( __in efx_txq_t *etp, __in unsigned int ns); extern void efx_tx_qpush( __in efx_txq_t *etp, __in unsigned int added, __in unsigned int pushed); extern __checkReturn efx_rc_t efx_tx_qflush( __in efx_txq_t *etp); extern void efx_tx_qenable( __in efx_txq_t *etp); extern __checkReturn efx_rc_t efx_tx_qpio_enable( __in efx_txq_t *etp); extern void efx_tx_qpio_disable( __in efx_txq_t *etp); extern __checkReturn efx_rc_t efx_tx_qpio_write( __in efx_txq_t *etp, __in_ecount(buf_length) uint8_t *buffer, __in size_t buf_length, __in size_t pio_buf_offset); extern __checkReturn efx_rc_t efx_tx_qpio_post( __in efx_txq_t *etp, __in size_t pkt_length, __in unsigned int completed, __inout unsigned int *addedp); extern __checkReturn efx_rc_t efx_tx_qdesc_post( __in efx_txq_t *etp, __in_ecount(n) efx_desc_t *ed, __in unsigned int n, __in unsigned int completed, __inout unsigned int *addedp); extern void efx_tx_qdesc_dma_create( __in efx_txq_t *etp, __in efsys_dma_addr_t addr, __in size_t size, __in boolean_t eop, __out efx_desc_t *edp); extern void efx_tx_qdesc_tso_create( __in efx_txq_t *etp, __in uint16_t ipv4_id, __in uint32_t tcp_seq, __in uint8_t tcp_flags, __out efx_desc_t *edp); /* Number of FATSOv2 option descriptors */ #define EFX_TX_FATSOV2_OPT_NDESCS 2 /* Maximum number of DMA segments per TSO packet (not superframe) */ #define EFX_TX_FATSOV2_DMA_SEGS_PER_PKT_MAX 24 extern void efx_tx_qdesc_tso2_create( __in efx_txq_t *etp, __in uint16_t ipv4_id, __in uint16_t outer_ipv4_id, __in uint32_t tcp_seq, __in uint16_t tcp_mss, __out_ecount(count) efx_desc_t *edp, __in int count); extern void efx_tx_qdesc_vlantci_create( __in efx_txq_t *etp, __in uint16_t tci, __out efx_desc_t *edp); extern void efx_tx_qdesc_checksum_create( __in efx_txq_t *etp, __in uint16_t flags, __out efx_desc_t *edp); #if EFSYS_OPT_QSTATS #if EFSYS_OPT_NAMES extern const char * efx_tx_qstat_name( __in efx_nic_t *etp, __in unsigned int id); #endif /* EFSYS_OPT_NAMES */ extern void efx_tx_qstats_update( __in efx_txq_t *etp, __inout_ecount(TX_NQSTATS) efsys_stat_t *stat); #endif /* EFSYS_OPT_QSTATS */ extern void efx_tx_qdestroy( __in efx_txq_t *etp); /* FILTER */ #if EFSYS_OPT_FILTER #define EFX_ETHER_TYPE_IPV4 0x0800 #define EFX_ETHER_TYPE_IPV6 0x86DD #define EFX_IPPROTO_TCP 6 #define EFX_IPPROTO_UDP 17 #define EFX_IPPROTO_GRE 47 /* Use RSS to spread across multiple queues */ #define EFX_FILTER_FLAG_RX_RSS 0x01 /* Enable RX scatter */ #define EFX_FILTER_FLAG_RX_SCATTER 0x02 /* * Override an automatic filter (priority EFX_FILTER_PRI_AUTO). * May only be set by the filter implementation for each type. * A removal request will restore the automatic filter in its place. */ #define EFX_FILTER_FLAG_RX_OVER_AUTO 0x04 /* Filter is for RX */ #define EFX_FILTER_FLAG_RX 0x08 /* Filter is for TX */ #define EFX_FILTER_FLAG_TX 0x10 /* Set match flag on the received packet */ #define EFX_FILTER_FLAG_ACTION_FLAG 0x20 /* Set match mark on the received packet */ #define EFX_FILTER_FLAG_ACTION_MARK 0x40 typedef uint8_t efx_filter_flags_t; /* * Flags which specify the fields to match on. The values are the same as in the * MC_CMD_FILTER_OP/MC_CMD_FILTER_OP_EXT commands. */ /* Match by remote IP host address */ #define EFX_FILTER_MATCH_REM_HOST 0x00000001 /* Match by local IP host address */ #define EFX_FILTER_MATCH_LOC_HOST 0x00000002 /* Match by remote MAC address */ #define EFX_FILTER_MATCH_REM_MAC 0x00000004 /* Match by remote TCP/UDP port */ #define EFX_FILTER_MATCH_REM_PORT 0x00000008 /* Match by remote TCP/UDP port */ #define EFX_FILTER_MATCH_LOC_MAC 0x00000010 /* Match by local TCP/UDP port */ #define EFX_FILTER_MATCH_LOC_PORT 0x00000020 /* Match by Ether-type */ #define EFX_FILTER_MATCH_ETHER_TYPE 0x00000040 /* Match by inner VLAN ID */ #define EFX_FILTER_MATCH_INNER_VID 0x00000080 /* Match by outer VLAN ID */ #define EFX_FILTER_MATCH_OUTER_VID 0x00000100 /* Match by IP transport protocol */ #define EFX_FILTER_MATCH_IP_PROTO 0x00000200 /* Match by VNI or VSID */ #define EFX_FILTER_MATCH_VNI_OR_VSID 0x00000800 /* For encapsulated packets, match by inner frame local MAC address */ #define EFX_FILTER_MATCH_IFRM_LOC_MAC 0x00010000 /* For encapsulated packets, match all multicast inner frames */ #define EFX_FILTER_MATCH_IFRM_UNKNOWN_MCAST_DST 0x01000000 /* For encapsulated packets, match all unicast inner frames */ #define EFX_FILTER_MATCH_IFRM_UNKNOWN_UCAST_DST 0x02000000 /* * Match by encap type, this flag does not correspond to * the MCDI match flags and any unoccupied value may be used */ #define EFX_FILTER_MATCH_ENCAP_TYPE 0x20000000 /* Match otherwise-unmatched multicast and broadcast packets */ #define EFX_FILTER_MATCH_UNKNOWN_MCAST_DST 0x40000000 /* Match otherwise-unmatched unicast packets */ #define EFX_FILTER_MATCH_UNKNOWN_UCAST_DST 0x80000000 typedef uint32_t efx_filter_match_flags_t; typedef enum efx_filter_priority_s { EFX_FILTER_PRI_HINT = 0, /* Performance hint */ EFX_FILTER_PRI_AUTO, /* Automatic filter based on device * address list or hardware * requirements. This may only be used * by the filter implementation for * each NIC type. */ EFX_FILTER_PRI_MANUAL, /* Manually configured filter */ EFX_FILTER_PRI_REQUIRED, /* Required for correct behaviour of the * client (e.g. SR-IOV, HyperV VMQ etc.) */ } efx_filter_priority_t; /* * FIXME: All these fields are assumed to be in little-endian byte order. * It may be better for some to be big-endian. See bug42804. */ typedef struct efx_filter_spec_s { efx_filter_match_flags_t efs_match_flags; uint8_t efs_priority; efx_filter_flags_t efs_flags; uint16_t efs_dmaq_id; uint32_t efs_rss_context; uint32_t efs_mark; /* Fields below here are hashed for software filter lookup */ uint16_t efs_outer_vid; uint16_t efs_inner_vid; uint8_t efs_loc_mac[EFX_MAC_ADDR_LEN]; uint8_t efs_rem_mac[EFX_MAC_ADDR_LEN]; uint16_t efs_ether_type; uint8_t efs_ip_proto; efx_tunnel_protocol_t efs_encap_type; uint16_t efs_loc_port; uint16_t efs_rem_port; efx_oword_t efs_rem_host; efx_oword_t efs_loc_host; uint8_t efs_vni_or_vsid[EFX_VNI_OR_VSID_LEN]; uint8_t efs_ifrm_loc_mac[EFX_MAC_ADDR_LEN]; } efx_filter_spec_t; /* Default values for use in filter specifications */ #define EFX_FILTER_SPEC_RX_DMAQ_ID_DROP 0xfff #define EFX_FILTER_SPEC_VID_UNSPEC 0xffff extern __checkReturn efx_rc_t efx_filter_init( __in efx_nic_t *enp); extern void efx_filter_fini( __in efx_nic_t *enp); extern __checkReturn efx_rc_t efx_filter_insert( __in efx_nic_t *enp, __inout efx_filter_spec_t *spec); extern __checkReturn efx_rc_t efx_filter_remove( __in efx_nic_t *enp, __inout efx_filter_spec_t *spec); extern __checkReturn efx_rc_t efx_filter_restore( __in efx_nic_t *enp); extern __checkReturn efx_rc_t efx_filter_supported_filters( __in efx_nic_t *enp, __out_ecount(buffer_length) uint32_t *buffer, __in size_t buffer_length, __out size_t *list_lengthp); extern void efx_filter_spec_init_rx( __out efx_filter_spec_t *spec, __in efx_filter_priority_t priority, __in efx_filter_flags_t flags, __in efx_rxq_t *erp); extern void efx_filter_spec_init_tx( __out efx_filter_spec_t *spec, __in efx_txq_t *etp); extern __checkReturn efx_rc_t efx_filter_spec_set_ipv4_local( __inout efx_filter_spec_t *spec, __in uint8_t proto, __in uint32_t host, __in uint16_t port); extern __checkReturn efx_rc_t efx_filter_spec_set_ipv4_full( __inout efx_filter_spec_t *spec, __in uint8_t proto, __in uint32_t lhost, __in uint16_t lport, __in uint32_t rhost, __in uint16_t rport); extern __checkReturn efx_rc_t efx_filter_spec_set_eth_local( __inout efx_filter_spec_t *spec, __in uint16_t vid, __in const uint8_t *addr); extern void efx_filter_spec_set_ether_type( __inout efx_filter_spec_t *spec, __in uint16_t ether_type); extern __checkReturn efx_rc_t efx_filter_spec_set_uc_def( __inout efx_filter_spec_t *spec); extern __checkReturn efx_rc_t efx_filter_spec_set_mc_def( __inout efx_filter_spec_t *spec); typedef enum efx_filter_inner_frame_match_e { EFX_FILTER_INNER_FRAME_MATCH_OTHER = 0, EFX_FILTER_INNER_FRAME_MATCH_UNKNOWN_MCAST_DST, EFX_FILTER_INNER_FRAME_MATCH_UNKNOWN_UCAST_DST } efx_filter_inner_frame_match_t; extern __checkReturn efx_rc_t efx_filter_spec_set_encap_type( __inout efx_filter_spec_t *spec, __in efx_tunnel_protocol_t encap_type, __in efx_filter_inner_frame_match_t inner_frame_match); extern __checkReturn efx_rc_t efx_filter_spec_set_vxlan( __inout efx_filter_spec_t *spec, __in const uint8_t *vni, __in const uint8_t *inner_addr, __in const uint8_t *outer_addr); extern __checkReturn efx_rc_t efx_filter_spec_set_geneve( __inout efx_filter_spec_t *spec, __in const uint8_t *vni, __in const uint8_t *inner_addr, __in const uint8_t *outer_addr); extern __checkReturn efx_rc_t efx_filter_spec_set_nvgre( __inout efx_filter_spec_t *spec, __in const uint8_t *vsid, __in const uint8_t *inner_addr, __in const uint8_t *outer_addr); #if EFSYS_OPT_RX_SCALE extern __checkReturn efx_rc_t efx_filter_spec_set_rss_context( __inout efx_filter_spec_t *spec, __in uint32_t rss_context); #endif #endif /* EFSYS_OPT_FILTER */ /* HASH */ extern __checkReturn uint32_t efx_hash_dwords( __in_ecount(count) uint32_t const *input, __in size_t count, __in uint32_t init); extern __checkReturn uint32_t efx_hash_bytes( __in_ecount(length) uint8_t const *input, __in size_t length, __in uint32_t init); #if EFSYS_OPT_LICENSING /* LICENSING */ typedef struct efx_key_stats_s { uint32_t eks_valid; uint32_t eks_invalid; uint32_t eks_blacklisted; uint32_t eks_unverifiable; uint32_t eks_wrong_node; uint32_t eks_licensed_apps_lo; uint32_t eks_licensed_apps_hi; uint32_t eks_licensed_features_lo; uint32_t eks_licensed_features_hi; } efx_key_stats_t; extern __checkReturn efx_rc_t efx_lic_init( __in efx_nic_t *enp); extern void efx_lic_fini( __in efx_nic_t *enp); extern __checkReturn boolean_t efx_lic_check_support( __in efx_nic_t *enp); extern __checkReturn efx_rc_t efx_lic_update_licenses( __in efx_nic_t *enp); extern __checkReturn efx_rc_t efx_lic_get_key_stats( __in efx_nic_t *enp, __out efx_key_stats_t *ksp); extern __checkReturn efx_rc_t efx_lic_app_state( __in efx_nic_t *enp, __in uint64_t app_id, __out boolean_t *licensedp); extern __checkReturn efx_rc_t efx_lic_get_id( __in efx_nic_t *enp, __in size_t buffer_size, __out uint32_t *typep, __out size_t *lengthp, __out_opt uint8_t *bufferp); extern __checkReturn efx_rc_t efx_lic_find_start( __in efx_nic_t *enp, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size, __out uint32_t *startp); extern __checkReturn efx_rc_t efx_lic_find_end( __in efx_nic_t *enp, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size, __in uint32_t offset, __out uint32_t *endp); extern __checkReturn __success(return != B_FALSE) boolean_t efx_lic_find_key( __in efx_nic_t *enp, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size, __in uint32_t offset, __out uint32_t *startp, __out uint32_t *lengthp); extern __checkReturn __success(return != B_FALSE) boolean_t efx_lic_validate_key( __in efx_nic_t *enp, __in_bcount(length) caddr_t keyp, __in uint32_t length); extern __checkReturn efx_rc_t efx_lic_read_key( __in efx_nic_t *enp, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size, __in uint32_t offset, __in uint32_t length, __out_bcount_part(key_max_size, *lengthp) caddr_t keyp, __in size_t key_max_size, __out uint32_t *lengthp); extern __checkReturn efx_rc_t efx_lic_write_key( __in efx_nic_t *enp, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size, __in uint32_t offset, __in_bcount(length) caddr_t keyp, __in uint32_t length, __out uint32_t *lengthp); __checkReturn efx_rc_t efx_lic_delete_key( __in efx_nic_t *enp, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size, __in uint32_t offset, __in uint32_t length, __in uint32_t end, __out uint32_t *deltap); extern __checkReturn efx_rc_t efx_lic_create_partition( __in efx_nic_t *enp, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size); extern __checkReturn efx_rc_t efx_lic_finish_partition( __in efx_nic_t *enp, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size); #endif /* EFSYS_OPT_LICENSING */ /* TUNNEL */ #if EFSYS_OPT_TUNNEL extern __checkReturn efx_rc_t efx_tunnel_init( __in efx_nic_t *enp); extern void efx_tunnel_fini( __in efx_nic_t *enp); /* * For overlay network encapsulation using UDP, the firmware needs to know * the configured UDP port for the overlay so it can decode encapsulated * frames correctly. * The UDP port/protocol list is global. */ extern __checkReturn efx_rc_t efx_tunnel_config_udp_add( __in efx_nic_t *enp, __in uint16_t port /* host/cpu-endian */, __in efx_tunnel_protocol_t protocol); extern __checkReturn efx_rc_t efx_tunnel_config_udp_remove( __in efx_nic_t *enp, __in uint16_t port /* host/cpu-endian */, __in efx_tunnel_protocol_t protocol); extern void efx_tunnel_config_clear( __in efx_nic_t *enp); /** * Apply tunnel UDP ports configuration to hardware. * * EAGAIN is returned if hardware will be reset (datapath and management CPU * reboot). */ extern __checkReturn efx_rc_t efx_tunnel_reconfigure( __in efx_nic_t *enp); #endif /* EFSYS_OPT_TUNNEL */ #if EFSYS_OPT_FW_SUBVARIANT_AWARE /** * Firmware subvariant choice options. * * It may be switched to no Tx checksum if attached drivers are either * preboot or firmware subvariant aware and no VIS are allocated. * If may be always switched to default explicitly using set request or * implicitly if unaware driver is attaching. If switching is done when * a driver is attached, it gets MC_REBOOT event and should recreate its * datapath. * * See SF-119419-TC DPDK Firmware Driver Interface and * SF-109306-TC EF10 for Driver Writers for details. */ typedef enum efx_nic_fw_subvariant_e { EFX_NIC_FW_SUBVARIANT_DEFAULT = 0, EFX_NIC_FW_SUBVARIANT_NO_TX_CSUM = 1, EFX_NIC_FW_SUBVARIANT_NTYPES } efx_nic_fw_subvariant_t; extern __checkReturn efx_rc_t efx_nic_get_fw_subvariant( __in efx_nic_t *enp, __out efx_nic_fw_subvariant_t *subvariantp); extern __checkReturn efx_rc_t efx_nic_set_fw_subvariant( __in efx_nic_t *enp, __in efx_nic_fw_subvariant_t subvariant); #endif /* EFSYS_OPT_FW_SUBVARIANT_AWARE */ typedef enum efx_phy_fec_type_e { EFX_PHY_FEC_NONE = 0, EFX_PHY_FEC_BASER, EFX_PHY_FEC_RS } efx_phy_fec_type_t; extern __checkReturn efx_rc_t efx_phy_fec_type_get( __in efx_nic_t *enp, __out efx_phy_fec_type_t *typep); typedef struct efx_phy_link_state_s { uint32_t epls_adv_cap_mask; uint32_t epls_lp_cap_mask; uint32_t epls_ld_cap_mask; unsigned int epls_fcntl; efx_phy_fec_type_t epls_fec; efx_link_mode_t epls_link_mode; } efx_phy_link_state_t; extern __checkReturn efx_rc_t efx_phy_link_state_get( __in efx_nic_t *enp, __out efx_phy_link_state_t *eplsp); #ifdef __cplusplus } #endif #endif /* _SYS_EFX_H */ Index: projects/clang700-import/sys/dev/sfxge/common/efx_nic.c =================================================================== --- projects/clang700-import/sys/dev/sfxge/common/efx_nic.c (revision 341812) +++ projects/clang700-import/sys/dev/sfxge/common/efx_nic.c (revision 341813) @@ -1,1145 +1,1148 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2007-2016 Solarflare Communications Inc. * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the FreeBSD Project. */ #include __FBSDID("$FreeBSD$"); #include "efx.h" #include "efx_impl.h" __checkReturn efx_rc_t efx_family( __in uint16_t venid, __in uint16_t devid, __out efx_family_t *efp, __out unsigned int *membarp) { if (venid == EFX_PCI_VENID_SFC) { switch (devid) { #if EFSYS_OPT_SIENA case EFX_PCI_DEVID_SIENA_F1_UNINIT: /* * Hardware default for PF0 of uninitialised Siena. * manftest must be able to cope with this device id. */ case EFX_PCI_DEVID_BETHPAGE: case EFX_PCI_DEVID_SIENA: *efp = EFX_FAMILY_SIENA; *membarp = EFX_MEM_BAR_SIENA; return (0); #endif /* EFSYS_OPT_SIENA */ #if EFSYS_OPT_HUNTINGTON case EFX_PCI_DEVID_HUNTINGTON_PF_UNINIT: /* * Hardware default for PF0 of uninitialised Huntington. * manftest must be able to cope with this device id. */ case EFX_PCI_DEVID_FARMINGDALE: case EFX_PCI_DEVID_GREENPORT: *efp = EFX_FAMILY_HUNTINGTON; *membarp = EFX_MEM_BAR_HUNTINGTON_PF; return (0); case EFX_PCI_DEVID_FARMINGDALE_VF: case EFX_PCI_DEVID_GREENPORT_VF: *efp = EFX_FAMILY_HUNTINGTON; *membarp = EFX_MEM_BAR_HUNTINGTON_VF; return (0); #endif /* EFSYS_OPT_HUNTINGTON */ #if EFSYS_OPT_MEDFORD case EFX_PCI_DEVID_MEDFORD_PF_UNINIT: /* * Hardware default for PF0 of uninitialised Medford. * manftest must be able to cope with this device id. */ case EFX_PCI_DEVID_MEDFORD: *efp = EFX_FAMILY_MEDFORD; *membarp = EFX_MEM_BAR_MEDFORD_PF; return (0); case EFX_PCI_DEVID_MEDFORD_VF: *efp = EFX_FAMILY_MEDFORD; *membarp = EFX_MEM_BAR_MEDFORD_VF; return (0); #endif /* EFSYS_OPT_MEDFORD */ #if EFSYS_OPT_MEDFORD2 case EFX_PCI_DEVID_MEDFORD2_PF_UNINIT: /* * Hardware default for PF0 of uninitialised Medford2. * manftest must be able to cope with this device id. */ case EFX_PCI_DEVID_MEDFORD2: case EFX_PCI_DEVID_MEDFORD2_VF: *efp = EFX_FAMILY_MEDFORD2; *membarp = EFX_MEM_BAR_MEDFORD2; return (0); #endif /* EFSYS_OPT_MEDFORD2 */ case EFX_PCI_DEVID_FALCON: /* Obsolete, not supported */ default: break; } } *efp = EFX_FAMILY_INVALID; return (ENOTSUP); } #if EFSYS_OPT_SIENA static const efx_nic_ops_t __efx_nic_siena_ops = { siena_nic_probe, /* eno_probe */ NULL, /* eno_board_cfg */ NULL, /* eno_set_drv_limits */ siena_nic_reset, /* eno_reset */ siena_nic_init, /* eno_init */ NULL, /* eno_get_vi_pool */ NULL, /* eno_get_bar_region */ NULL, /* eno_hw_unavailable */ NULL, /* eno_set_hw_unavailable */ #if EFSYS_OPT_DIAG siena_nic_register_test, /* eno_register_test */ #endif /* EFSYS_OPT_DIAG */ siena_nic_fini, /* eno_fini */ siena_nic_unprobe, /* eno_unprobe */ }; #endif /* EFSYS_OPT_SIENA */ #if EFSYS_OPT_HUNTINGTON static const efx_nic_ops_t __efx_nic_hunt_ops = { ef10_nic_probe, /* eno_probe */ hunt_board_cfg, /* eno_board_cfg */ ef10_nic_set_drv_limits, /* eno_set_drv_limits */ ef10_nic_reset, /* eno_reset */ ef10_nic_init, /* eno_init */ ef10_nic_get_vi_pool, /* eno_get_vi_pool */ ef10_nic_get_bar_region, /* eno_get_bar_region */ ef10_nic_hw_unavailable, /* eno_hw_unavailable */ ef10_nic_set_hw_unavailable, /* eno_set_hw_unavailable */ #if EFSYS_OPT_DIAG ef10_nic_register_test, /* eno_register_test */ #endif /* EFSYS_OPT_DIAG */ ef10_nic_fini, /* eno_fini */ ef10_nic_unprobe, /* eno_unprobe */ }; #endif /* EFSYS_OPT_HUNTINGTON */ #if EFSYS_OPT_MEDFORD static const efx_nic_ops_t __efx_nic_medford_ops = { ef10_nic_probe, /* eno_probe */ medford_board_cfg, /* eno_board_cfg */ ef10_nic_set_drv_limits, /* eno_set_drv_limits */ ef10_nic_reset, /* eno_reset */ ef10_nic_init, /* eno_init */ ef10_nic_get_vi_pool, /* eno_get_vi_pool */ ef10_nic_get_bar_region, /* eno_get_bar_region */ ef10_nic_hw_unavailable, /* eno_hw_unavailable */ ef10_nic_set_hw_unavailable, /* eno_set_hw_unavailable */ #if EFSYS_OPT_DIAG ef10_nic_register_test, /* eno_register_test */ #endif /* EFSYS_OPT_DIAG */ ef10_nic_fini, /* eno_fini */ ef10_nic_unprobe, /* eno_unprobe */ }; #endif /* EFSYS_OPT_MEDFORD */ #if EFSYS_OPT_MEDFORD2 static const efx_nic_ops_t __efx_nic_medford2_ops = { ef10_nic_probe, /* eno_probe */ medford2_board_cfg, /* eno_board_cfg */ ef10_nic_set_drv_limits, /* eno_set_drv_limits */ ef10_nic_reset, /* eno_reset */ ef10_nic_init, /* eno_init */ ef10_nic_get_vi_pool, /* eno_get_vi_pool */ ef10_nic_get_bar_region, /* eno_get_bar_region */ ef10_nic_hw_unavailable, /* eno_hw_unavailable */ ef10_nic_set_hw_unavailable, /* eno_set_hw_unavailable */ #if EFSYS_OPT_DIAG ef10_nic_register_test, /* eno_register_test */ #endif /* EFSYS_OPT_DIAG */ ef10_nic_fini, /* eno_fini */ ef10_nic_unprobe, /* eno_unprobe */ }; #endif /* EFSYS_OPT_MEDFORD2 */ __checkReturn efx_rc_t efx_nic_create( __in efx_family_t family, __in efsys_identifier_t *esip, __in efsys_bar_t *esbp, __in efsys_lock_t *eslp, __deref_out efx_nic_t **enpp) { efx_nic_t *enp; efx_rc_t rc; EFSYS_ASSERT3U(family, >, EFX_FAMILY_INVALID); EFSYS_ASSERT3U(family, <, EFX_FAMILY_NTYPES); /* Allocate a NIC object */ EFSYS_KMEM_ALLOC(esip, sizeof (efx_nic_t), enp); if (enp == NULL) { rc = ENOMEM; goto fail1; } enp->en_magic = EFX_NIC_MAGIC; switch (family) { #if EFSYS_OPT_SIENA case EFX_FAMILY_SIENA: enp->en_enop = &__efx_nic_siena_ops; enp->en_features = EFX_FEATURE_IPV6 | EFX_FEATURE_LFSR_HASH_INSERT | EFX_FEATURE_LINK_EVENTS | EFX_FEATURE_PERIODIC_MAC_STATS | EFX_FEATURE_MCDI | EFX_FEATURE_LOOKAHEAD_SPLIT | EFX_FEATURE_MAC_HEADER_FILTERS | EFX_FEATURE_TX_SRC_FILTERS; break; #endif /* EFSYS_OPT_SIENA */ #if EFSYS_OPT_HUNTINGTON case EFX_FAMILY_HUNTINGTON: enp->en_enop = &__efx_nic_hunt_ops; enp->en_features = EFX_FEATURE_IPV6 | EFX_FEATURE_LINK_EVENTS | EFX_FEATURE_PERIODIC_MAC_STATS | EFX_FEATURE_MCDI | EFX_FEATURE_MAC_HEADER_FILTERS | EFX_FEATURE_MCDI_DMA | EFX_FEATURE_PIO_BUFFERS | EFX_FEATURE_FW_ASSISTED_TSO | EFX_FEATURE_FW_ASSISTED_TSO_V2 | - EFX_FEATURE_PACKED_STREAM; + EFX_FEATURE_PACKED_STREAM | + EFX_FEATURE_TXQ_CKSUM_OP_DESC; break; #endif /* EFSYS_OPT_HUNTINGTON */ #if EFSYS_OPT_MEDFORD case EFX_FAMILY_MEDFORD: enp->en_enop = &__efx_nic_medford_ops; /* * FW_ASSISTED_TSO omitted as Medford only supports firmware * assisted TSO version 2, not the v1 scheme used on Huntington. */ enp->en_features = EFX_FEATURE_IPV6 | EFX_FEATURE_LINK_EVENTS | EFX_FEATURE_PERIODIC_MAC_STATS | EFX_FEATURE_MCDI | EFX_FEATURE_MAC_HEADER_FILTERS | EFX_FEATURE_MCDI_DMA | EFX_FEATURE_PIO_BUFFERS | EFX_FEATURE_FW_ASSISTED_TSO_V2 | - EFX_FEATURE_PACKED_STREAM; + EFX_FEATURE_PACKED_STREAM | + EFX_FEATURE_TXQ_CKSUM_OP_DESC; break; #endif /* EFSYS_OPT_MEDFORD */ #if EFSYS_OPT_MEDFORD2 case EFX_FAMILY_MEDFORD2: enp->en_enop = &__efx_nic_medford2_ops; enp->en_features = EFX_FEATURE_IPV6 | EFX_FEATURE_LINK_EVENTS | EFX_FEATURE_PERIODIC_MAC_STATS | EFX_FEATURE_MCDI | EFX_FEATURE_MAC_HEADER_FILTERS | EFX_FEATURE_MCDI_DMA | EFX_FEATURE_PIO_BUFFERS | EFX_FEATURE_FW_ASSISTED_TSO_V2 | - EFX_FEATURE_PACKED_STREAM; + EFX_FEATURE_PACKED_STREAM | + EFX_FEATURE_TXQ_CKSUM_OP_DESC; break; #endif /* EFSYS_OPT_MEDFORD2 */ default: rc = ENOTSUP; goto fail2; } enp->en_family = family; enp->en_esip = esip; enp->en_esbp = esbp; enp->en_eslp = eslp; *enpp = enp; return (0); fail2: EFSYS_PROBE(fail2); enp->en_magic = 0; /* Free the NIC object */ EFSYS_KMEM_FREE(esip, sizeof (efx_nic_t), enp); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t efx_nic_probe( __in efx_nic_t *enp, __in efx_fw_variant_t efv) { const efx_nic_ops_t *enop; efx_rc_t rc; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); #if EFSYS_OPT_MCDI EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); #endif /* EFSYS_OPT_MCDI */ EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_PROBE)); /* Ensure FW variant codes match with MC_CMD_FW codes */ EFX_STATIC_ASSERT(EFX_FW_VARIANT_FULL_FEATURED == MC_CMD_FW_FULL_FEATURED); EFX_STATIC_ASSERT(EFX_FW_VARIANT_LOW_LATENCY == MC_CMD_FW_LOW_LATENCY); EFX_STATIC_ASSERT(EFX_FW_VARIANT_PACKED_STREAM == MC_CMD_FW_PACKED_STREAM); EFX_STATIC_ASSERT(EFX_FW_VARIANT_HIGH_TX_RATE == MC_CMD_FW_HIGH_TX_RATE); EFX_STATIC_ASSERT(EFX_FW_VARIANT_PACKED_STREAM_HASH_MODE_1 == MC_CMD_FW_PACKED_STREAM_HASH_MODE_1); EFX_STATIC_ASSERT(EFX_FW_VARIANT_RULES_ENGINE == MC_CMD_FW_RULES_ENGINE); EFX_STATIC_ASSERT(EFX_FW_VARIANT_DPDK == MC_CMD_FW_DPDK); EFX_STATIC_ASSERT(EFX_FW_VARIANT_DONT_CARE == (int)MC_CMD_FW_DONT_CARE); enop = enp->en_enop; enp->efv = efv; if ((rc = enop->eno_probe(enp)) != 0) goto fail1; if ((rc = efx_phy_probe(enp)) != 0) goto fail2; enp->en_mod_flags |= EFX_MOD_PROBE; return (0); fail2: EFSYS_PROBE(fail2); enop->eno_unprobe(enp); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t efx_nic_set_drv_limits( __inout efx_nic_t *enp, __in efx_drv_limits_t *edlp) { const efx_nic_ops_t *enop = enp->en_enop; efx_rc_t rc; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); if (enop->eno_set_drv_limits != NULL) { if ((rc = enop->eno_set_drv_limits(enp, edlp)) != 0) goto fail1; } return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t efx_nic_get_bar_region( __in efx_nic_t *enp, __in efx_nic_region_t region, __out uint32_t *offsetp, __out size_t *sizep) { const efx_nic_ops_t *enop = enp->en_enop; efx_rc_t rc; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NIC); if (enop->eno_get_bar_region == NULL) { rc = ENOTSUP; goto fail1; } if ((rc = (enop->eno_get_bar_region)(enp, region, offsetp, sizep)) != 0) { goto fail2; } return (0); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t efx_nic_get_vi_pool( __in efx_nic_t *enp, __out uint32_t *evq_countp, __out uint32_t *rxq_countp, __out uint32_t *txq_countp) { const efx_nic_ops_t *enop = enp->en_enop; efx_nic_cfg_t *encp = &enp->en_nic_cfg; efx_rc_t rc; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NIC); if (enop->eno_get_vi_pool != NULL) { uint32_t vi_count = 0; if ((rc = (enop->eno_get_vi_pool)(enp, &vi_count)) != 0) goto fail1; *evq_countp = vi_count; *rxq_countp = vi_count; *txq_countp = vi_count; } else { /* Use NIC limits as default value */ *evq_countp = encp->enc_evq_limit; *rxq_countp = encp->enc_rxq_limit; *txq_countp = encp->enc_txq_limit; } return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t efx_nic_init( __in efx_nic_t *enp) { const efx_nic_ops_t *enop = enp->en_enop; efx_rc_t rc; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); if (enp->en_mod_flags & EFX_MOD_NIC) { rc = EINVAL; goto fail1; } if ((rc = enop->eno_init(enp)) != 0) goto fail2; enp->en_mod_flags |= EFX_MOD_NIC; return (0); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } void efx_nic_fini( __in efx_nic_t *enp) { const efx_nic_ops_t *enop = enp->en_enop; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_PROBE); EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_NIC); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_INTR)); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_EV)); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_RX)); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TX)); enop->eno_fini(enp); enp->en_mod_flags &= ~EFX_MOD_NIC; } void efx_nic_unprobe( __in efx_nic_t *enp) { const efx_nic_ops_t *enop = enp->en_enop; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); #if EFSYS_OPT_MCDI EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); #endif /* EFSYS_OPT_MCDI */ EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC)); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_INTR)); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_EV)); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_RX)); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TX)); efx_phy_unprobe(enp); enop->eno_unprobe(enp); enp->en_mod_flags &= ~EFX_MOD_PROBE; } void efx_nic_destroy( __in efx_nic_t *enp) { efsys_identifier_t *esip = enp->en_esip; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0); enp->en_family = EFX_FAMILY_INVALID; enp->en_esip = NULL; enp->en_esbp = NULL; enp->en_eslp = NULL; enp->en_enop = NULL; enp->en_magic = 0; /* Free the NIC object */ EFSYS_KMEM_FREE(esip, sizeof (efx_nic_t), enp); } __checkReturn efx_rc_t efx_nic_reset( __in efx_nic_t *enp) { const efx_nic_ops_t *enop = enp->en_enop; unsigned int mod_flags; efx_rc_t rc; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_PROBE); /* * All modules except the MCDI, PROBE, NVRAM, VPD, MON, TUNNEL * (which we do not reset here) must have been shut down or never * initialized. * * A rule of thumb here is: If the controller or MC reboots, is *any* * state lost. If it's lost and needs reapplying, then the module * *must* not be initialised during the reset. */ mod_flags = enp->en_mod_flags; mod_flags &= ~(EFX_MOD_MCDI | EFX_MOD_PROBE | EFX_MOD_NVRAM | EFX_MOD_VPD | EFX_MOD_MON); #if EFSYS_OPT_TUNNEL mod_flags &= ~EFX_MOD_TUNNEL; #endif /* EFSYS_OPT_TUNNEL */ EFSYS_ASSERT3U(mod_flags, ==, 0); if (mod_flags != 0) { rc = EINVAL; goto fail1; } if ((rc = enop->eno_reset(enp)) != 0) goto fail2; return (0); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } const efx_nic_cfg_t * efx_nic_cfg_get( __in efx_nic_t *enp) { EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); return (&(enp->en_nic_cfg)); } __checkReturn efx_rc_t efx_nic_get_fw_version( __in efx_nic_t *enp, __out efx_nic_fw_info_t *enfip) { uint16_t mc_fw_version[4]; efx_rc_t rc; if (enfip == NULL) { rc = EINVAL; goto fail1; } EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); /* Ensure RXDP_FW_ID codes match with MC_CMD_GET_CAPABILITIES codes */ EFX_STATIC_ASSERT(EFX_RXDP_FULL_FEATURED_FW_ID == MC_CMD_GET_CAPABILITIES_OUT_RXDP); EFX_STATIC_ASSERT(EFX_RXDP_LOW_LATENCY_FW_ID == MC_CMD_GET_CAPABILITIES_OUT_RXDP_LOW_LATENCY); EFX_STATIC_ASSERT(EFX_RXDP_PACKED_STREAM_FW_ID == MC_CMD_GET_CAPABILITIES_OUT_RXDP_PACKED_STREAM); EFX_STATIC_ASSERT(EFX_RXDP_RULES_ENGINE_FW_ID == MC_CMD_GET_CAPABILITIES_OUT_RXDP_RULES_ENGINE); EFX_STATIC_ASSERT(EFX_RXDP_DPDK_FW_ID == MC_CMD_GET_CAPABILITIES_OUT_RXDP_DPDK); rc = efx_mcdi_version(enp, mc_fw_version, NULL, NULL); if (rc != 0) goto fail2; rc = efx_mcdi_get_capabilities(enp, NULL, &enfip->enfi_rx_dpcpu_fw_id, &enfip->enfi_tx_dpcpu_fw_id, NULL, NULL); if (rc == 0) { enfip->enfi_dpcpu_fw_ids_valid = B_TRUE; } else if (rc == ENOTSUP) { enfip->enfi_dpcpu_fw_ids_valid = B_FALSE; enfip->enfi_rx_dpcpu_fw_id = 0; enfip->enfi_tx_dpcpu_fw_id = 0; } else { goto fail3; } memcpy(enfip->enfi_mc_fw_version, mc_fw_version, sizeof (mc_fw_version)); return (0); fail3: EFSYS_PROBE(fail3); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn boolean_t efx_nic_hw_unavailable( __in efx_nic_t *enp) { const efx_nic_ops_t *enop = enp->en_enop; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); /* NOTE: can be used by MCDI before NIC probe */ if (enop->eno_hw_unavailable != NULL) { if ((enop->eno_hw_unavailable)(enp) != B_FALSE) goto unavail; } return (B_FALSE); unavail: return (B_TRUE); } void efx_nic_set_hw_unavailable( __in efx_nic_t *enp) { const efx_nic_ops_t *enop = enp->en_enop; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); if (enop->eno_set_hw_unavailable != NULL) enop->eno_set_hw_unavailable(enp); } #if EFSYS_OPT_DIAG __checkReturn efx_rc_t efx_nic_register_test( __in efx_nic_t *enp) { const efx_nic_ops_t *enop = enp->en_enop; efx_rc_t rc; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC)); if ((rc = enop->eno_register_test(enp)) != 0) goto fail1; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } #endif /* EFSYS_OPT_DIAG */ #if EFSYS_OPT_LOOPBACK extern void efx_loopback_mask( __in efx_loopback_kind_t loopback_kind, __out efx_qword_t *maskp) { efx_qword_t mask; EFSYS_ASSERT3U(loopback_kind, <, EFX_LOOPBACK_NKINDS); EFSYS_ASSERT(maskp != NULL); /* Assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespaces agree */ #define LOOPBACK_CHECK(_mcdi, _efx) \ EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_##_mcdi == EFX_LOOPBACK_##_efx) LOOPBACK_CHECK(NONE, OFF); LOOPBACK_CHECK(DATA, DATA); LOOPBACK_CHECK(GMAC, GMAC); LOOPBACK_CHECK(XGMII, XGMII); LOOPBACK_CHECK(XGXS, XGXS); LOOPBACK_CHECK(XAUI, XAUI); LOOPBACK_CHECK(GMII, GMII); LOOPBACK_CHECK(SGMII, SGMII); LOOPBACK_CHECK(XGBR, XGBR); LOOPBACK_CHECK(XFI, XFI); LOOPBACK_CHECK(XAUI_FAR, XAUI_FAR); LOOPBACK_CHECK(GMII_FAR, GMII_FAR); LOOPBACK_CHECK(SGMII_FAR, SGMII_FAR); LOOPBACK_CHECK(XFI_FAR, XFI_FAR); LOOPBACK_CHECK(GPHY, GPHY); LOOPBACK_CHECK(PHYXS, PHY_XS); LOOPBACK_CHECK(PCS, PCS); LOOPBACK_CHECK(PMAPMD, PMA_PMD); LOOPBACK_CHECK(XPORT, XPORT); LOOPBACK_CHECK(XGMII_WS, XGMII_WS); LOOPBACK_CHECK(XAUI_WS, XAUI_WS); LOOPBACK_CHECK(XAUI_WS_FAR, XAUI_WS_FAR); LOOPBACK_CHECK(XAUI_WS_NEAR, XAUI_WS_NEAR); LOOPBACK_CHECK(GMII_WS, GMII_WS); LOOPBACK_CHECK(XFI_WS, XFI_WS); LOOPBACK_CHECK(XFI_WS_FAR, XFI_WS_FAR); LOOPBACK_CHECK(PHYXS_WS, PHYXS_WS); LOOPBACK_CHECK(PMA_INT, PMA_INT); LOOPBACK_CHECK(SD_NEAR, SD_NEAR); LOOPBACK_CHECK(SD_FAR, SD_FAR); LOOPBACK_CHECK(PMA_INT_WS, PMA_INT_WS); LOOPBACK_CHECK(SD_FEP2_WS, SD_FEP2_WS); LOOPBACK_CHECK(SD_FEP1_5_WS, SD_FEP1_5_WS); LOOPBACK_CHECK(SD_FEP_WS, SD_FEP_WS); LOOPBACK_CHECK(SD_FES_WS, SD_FES_WS); LOOPBACK_CHECK(AOE_INT_NEAR, AOE_INT_NEAR); LOOPBACK_CHECK(DATA_WS, DATA_WS); LOOPBACK_CHECK(FORCE_EXT_LINK, FORCE_EXT_LINK); #undef LOOPBACK_CHECK /* Build bitmask of possible loopback types */ EFX_ZERO_QWORD(mask); if ((loopback_kind == EFX_LOOPBACK_KIND_OFF) || (loopback_kind == EFX_LOOPBACK_KIND_ALL)) { EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_OFF); } if ((loopback_kind == EFX_LOOPBACK_KIND_MAC) || (loopback_kind == EFX_LOOPBACK_KIND_ALL)) { /* * The "MAC" grouping has historically been used by drivers to * mean loopbacks supported by on-chip hardware. Keep that * meaning here, and include on-chip PHY layer loopbacks. */ EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_DATA); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMAC); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGMII); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGXS); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XAUI); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMII); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SGMII); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGBR); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XFI); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XAUI_FAR); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMII_FAR); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SGMII_FAR); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XFI_FAR); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PMA_INT); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SD_NEAR); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SD_FAR); } if ((loopback_kind == EFX_LOOPBACK_KIND_PHY) || (loopback_kind == EFX_LOOPBACK_KIND_ALL)) { /* * The "PHY" grouping has historically been used by drivers to * mean loopbacks supported by off-chip hardware. Keep that * meaning here. */ EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GPHY); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PHY_XS); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PCS); EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PMA_PMD); } *maskp = mask; } __checkReturn efx_rc_t efx_mcdi_get_loopback_modes( __in efx_nic_t *enp) { efx_nic_cfg_t *encp = &(enp->en_nic_cfg); efx_mcdi_req_t req; EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_LOOPBACK_MODES_IN_LEN, MC_CMD_GET_LOOPBACK_MODES_OUT_V2_LEN); efx_qword_t mask; efx_qword_t modes; efx_rc_t rc; req.emr_cmd = MC_CMD_GET_LOOPBACK_MODES; req.emr_in_buf = payload; req.emr_in_length = MC_CMD_GET_LOOPBACK_MODES_IN_LEN; req.emr_out_buf = payload; req.emr_out_length = MC_CMD_GET_LOOPBACK_MODES_OUT_V2_LEN; efx_mcdi_execute(enp, &req); if (req.emr_rc != 0) { rc = req.emr_rc; goto fail1; } if (req.emr_out_length_used < MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_OFST + MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_LEN) { rc = EMSGSIZE; goto fail2; } /* * We assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespaces agree * in efx_loopback_mask() and in siena_phy.c:siena_phy_get_link(). */ efx_loopback_mask(EFX_LOOPBACK_KIND_ALL, &mask); EFX_AND_QWORD(mask, *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_SUGGESTED)); modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_100M); EFX_AND_QWORD(modes, mask); encp->enc_loopback_types[EFX_LINK_100FDX] = modes; modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_1G); EFX_AND_QWORD(modes, mask); encp->enc_loopback_types[EFX_LINK_1000FDX] = modes; modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_10G); EFX_AND_QWORD(modes, mask); encp->enc_loopback_types[EFX_LINK_10000FDX] = modes; if (req.emr_out_length_used >= MC_CMD_GET_LOOPBACK_MODES_OUT_40G_OFST + MC_CMD_GET_LOOPBACK_MODES_OUT_40G_LEN) { /* Response includes 40G loopback modes */ modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_40G); EFX_AND_QWORD(modes, mask); encp->enc_loopback_types[EFX_LINK_40000FDX] = modes; } if (req.emr_out_length_used >= MC_CMD_GET_LOOPBACK_MODES_OUT_V2_25G_OFST + MC_CMD_GET_LOOPBACK_MODES_OUT_V2_25G_LEN) { /* Response includes 25G loopback modes */ modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_V2_25G); EFX_AND_QWORD(modes, mask); encp->enc_loopback_types[EFX_LINK_25000FDX] = modes; } if (req.emr_out_length_used >= MC_CMD_GET_LOOPBACK_MODES_OUT_V2_50G_OFST + MC_CMD_GET_LOOPBACK_MODES_OUT_V2_50G_LEN) { /* Response includes 50G loopback modes */ modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_V2_50G); EFX_AND_QWORD(modes, mask); encp->enc_loopback_types[EFX_LINK_50000FDX] = modes; } if (req.emr_out_length_used >= MC_CMD_GET_LOOPBACK_MODES_OUT_V2_100G_OFST + MC_CMD_GET_LOOPBACK_MODES_OUT_V2_100G_LEN) { /* Response includes 100G loopback modes */ modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_V2_100G); EFX_AND_QWORD(modes, mask); encp->enc_loopback_types[EFX_LINK_100000FDX] = modes; } EFX_ZERO_QWORD(modes); EFX_SET_QWORD_BIT(modes, EFX_LOOPBACK_OFF); EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_100FDX]); EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_1000FDX]); EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_10000FDX]); EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_40000FDX]); EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_25000FDX]); EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_50000FDX]); EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_100000FDX]); encp->enc_loopback_types[EFX_LINK_UNKNOWN] = modes; return (0); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } #endif /* EFSYS_OPT_LOOPBACK */ __checkReturn efx_rc_t efx_nic_calculate_pcie_link_bandwidth( __in uint32_t pcie_link_width, __in uint32_t pcie_link_gen, __out uint32_t *bandwidth_mbpsp) { uint32_t lane_bandwidth; uint32_t total_bandwidth; efx_rc_t rc; if ((pcie_link_width == 0) || (pcie_link_width > 16) || !ISP2(pcie_link_width)) { rc = EINVAL; goto fail1; } switch (pcie_link_gen) { case EFX_PCIE_LINK_SPEED_GEN1: /* 2.5 Gb/s raw bandwidth with 8b/10b encoding */ lane_bandwidth = 2000; break; case EFX_PCIE_LINK_SPEED_GEN2: /* 5.0 Gb/s raw bandwidth with 8b/10b encoding */ lane_bandwidth = 4000; break; case EFX_PCIE_LINK_SPEED_GEN3: /* 8.0 Gb/s raw bandwidth with 128b/130b encoding */ lane_bandwidth = 7877; break; default: rc = EINVAL; goto fail2; } total_bandwidth = lane_bandwidth * pcie_link_width; *bandwidth_mbpsp = total_bandwidth; return (0); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } #if EFSYS_OPT_FW_SUBVARIANT_AWARE __checkReturn efx_rc_t efx_nic_get_fw_subvariant( __in efx_nic_t *enp, __out efx_nic_fw_subvariant_t *subvariantp) { efx_rc_t rc; uint32_t value; rc = efx_mcdi_get_nic_global(enp, MC_CMD_SET_NIC_GLOBAL_IN_FIRMWARE_SUBVARIANT, &value); if (rc != 0) goto fail1; /* Mapping is not required since values match MCDI */ EFX_STATIC_ASSERT(EFX_NIC_FW_SUBVARIANT_DEFAULT == MC_CMD_SET_NIC_GLOBAL_IN_FW_SUBVARIANT_DEFAULT); EFX_STATIC_ASSERT(EFX_NIC_FW_SUBVARIANT_NO_TX_CSUM == MC_CMD_SET_NIC_GLOBAL_IN_FW_SUBVARIANT_NO_TX_CSUM); switch (value) { case MC_CMD_SET_NIC_GLOBAL_IN_FW_SUBVARIANT_DEFAULT: case MC_CMD_SET_NIC_GLOBAL_IN_FW_SUBVARIANT_NO_TX_CSUM: *subvariantp = value; break; default: rc = EINVAL; goto fail2; } return (0); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t efx_nic_set_fw_subvariant( __in efx_nic_t *enp, __in efx_nic_fw_subvariant_t subvariant) { efx_rc_t rc; switch (subvariant) { case EFX_NIC_FW_SUBVARIANT_DEFAULT: case EFX_NIC_FW_SUBVARIANT_NO_TX_CSUM: /* Mapping is not required since values match MCDI */ break; default: rc = EINVAL; goto fail1; } rc = efx_mcdi_set_nic_global(enp, MC_CMD_SET_NIC_GLOBAL_IN_FIRMWARE_SUBVARIANT, subvariant); if (rc != 0) goto fail2; return (0); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } #endif /* EFSYS_OPT_FW_SUBVARIANT_AWARE */ __checkReturn efx_rc_t efx_nic_check_pcie_link_speed( __in efx_nic_t *enp, __in uint32_t pcie_link_width, __in uint32_t pcie_link_gen, __out efx_pcie_link_performance_t *resultp) { efx_nic_cfg_t *encp = &(enp->en_nic_cfg); uint32_t bandwidth; efx_pcie_link_performance_t result; efx_rc_t rc; if ((encp->enc_required_pcie_bandwidth_mbps == 0) || (pcie_link_width == 0) || (pcie_link_width == 32) || (pcie_link_gen == 0)) { /* * No usable info on what is required and/or in use. In virtual * machines, sometimes the PCIe link width is reported as 0 or * 32, or the speed as 0. */ result = EFX_PCIE_LINK_PERFORMANCE_UNKNOWN_BANDWIDTH; goto out; } /* Calculate the available bandwidth in megabits per second */ rc = efx_nic_calculate_pcie_link_bandwidth(pcie_link_width, pcie_link_gen, &bandwidth); if (rc != 0) goto fail1; if (bandwidth < encp->enc_required_pcie_bandwidth_mbps) { result = EFX_PCIE_LINK_PERFORMANCE_SUBOPTIMAL_BANDWIDTH; } else if (pcie_link_gen < encp->enc_max_pcie_link_gen) { /* The link provides enough bandwidth but not optimal latency */ result = EFX_PCIE_LINK_PERFORMANCE_SUBOPTIMAL_LATENCY; } else { result = EFX_PCIE_LINK_PERFORMANCE_OPTIMAL; } out: *resultp = result; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } Index: projects/clang700-import/sys/dev/sfxge/sfxge.c =================================================================== --- projects/clang700-import/sys/dev/sfxge/sfxge.c (revision 341812) +++ projects/clang700-import/sys/dev/sfxge/sfxge.c (revision 341813) @@ -1,1210 +1,1215 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010-2016 Solarflare Communications Inc. * All rights reserved. * * This software was developed in part by Philip Paeps under contract for * Solarflare Communications, 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. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the FreeBSD Project. */ #include __FBSDID("$FreeBSD$"); #include "opt_rss.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RSS #include #endif #include "common/efx.h" #include "sfxge.h" #include "sfxge_rx.h" #include "sfxge_ioc.h" #include "sfxge_version.h" #define SFXGE_CAP (IFCAP_VLAN_MTU | IFCAP_VLAN_HWCSUM | \ IFCAP_RXCSUM | IFCAP_TXCSUM | \ IFCAP_RXCSUM_IPV6 | IFCAP_TXCSUM_IPV6 | \ IFCAP_TSO4 | IFCAP_TSO6 | \ IFCAP_JUMBO_MTU | \ IFCAP_VLAN_HWTSO | IFCAP_LINKSTATE | IFCAP_HWSTATS) #define SFXGE_CAP_ENABLE SFXGE_CAP #define SFXGE_CAP_FIXED (IFCAP_VLAN_MTU | \ IFCAP_JUMBO_MTU | IFCAP_LINKSTATE | IFCAP_HWSTATS) MALLOC_DEFINE(M_SFXGE, "sfxge", "Solarflare 10GigE driver"); SYSCTL_NODE(_hw, OID_AUTO, sfxge, CTLFLAG_RD, 0, "SFXGE driver parameters"); #define SFXGE_PARAM_RX_RING SFXGE_PARAM(rx_ring) static int sfxge_rx_ring_entries = SFXGE_NDESCS; TUNABLE_INT(SFXGE_PARAM_RX_RING, &sfxge_rx_ring_entries); SYSCTL_INT(_hw_sfxge, OID_AUTO, rx_ring, CTLFLAG_RDTUN, &sfxge_rx_ring_entries, 0, "Maximum number of descriptors in a receive ring"); #define SFXGE_PARAM_TX_RING SFXGE_PARAM(tx_ring) static int sfxge_tx_ring_entries = SFXGE_NDESCS; TUNABLE_INT(SFXGE_PARAM_TX_RING, &sfxge_tx_ring_entries); SYSCTL_INT(_hw_sfxge, OID_AUTO, tx_ring, CTLFLAG_RDTUN, &sfxge_tx_ring_entries, 0, "Maximum number of descriptors in a transmit ring"); #define SFXGE_PARAM_RESTART_ATTEMPTS SFXGE_PARAM(restart_attempts) static int sfxge_restart_attempts = 3; TUNABLE_INT(SFXGE_PARAM_RESTART_ATTEMPTS, &sfxge_restart_attempts); SYSCTL_INT(_hw_sfxge, OID_AUTO, restart_attempts, CTLFLAG_RDTUN, &sfxge_restart_attempts, 0, "Maximum number of attempts to bring interface up after reset"); #if EFSYS_OPT_MCDI_LOGGING #define SFXGE_PARAM_MCDI_LOGGING SFXGE_PARAM(mcdi_logging) static int sfxge_mcdi_logging = 0; TUNABLE_INT(SFXGE_PARAM_MCDI_LOGGING, &sfxge_mcdi_logging); #endif static void sfxge_reset(void *arg, int npending); static int sfxge_estimate_rsrc_limits(struct sfxge_softc *sc) { efx_drv_limits_t limits; int rc; unsigned int evq_max; uint32_t evq_allocated; uint32_t rxq_allocated; uint32_t txq_allocated; /* * Limit the number of event queues to: * - number of CPUs * - hardwire maximum RSS channels * - administratively specified maximum RSS channels */ #ifdef RSS /* * Avoid extra limitations so that the number of queues * may be configured at administrator's will */ evq_max = MIN(MAX(rss_getnumbuckets(), 1), EFX_MAXRSS); #else evq_max = MIN(mp_ncpus, EFX_MAXRSS); #endif if (sc->max_rss_channels > 0) evq_max = MIN(evq_max, sc->max_rss_channels); memset(&limits, 0, sizeof(limits)); limits.edl_min_evq_count = 1; limits.edl_max_evq_count = evq_max; - limits.edl_min_txq_count = SFXGE_TXQ_NTYPES; - limits.edl_max_txq_count = evq_max + SFXGE_TXQ_NTYPES - 1; + limits.edl_min_txq_count = SFXGE_EVQ0_N_TXQ(sc); + limits.edl_max_txq_count = evq_max + SFXGE_EVQ0_N_TXQ(sc) - 1; limits.edl_min_rxq_count = 1; limits.edl_max_rxq_count = evq_max; efx_nic_set_drv_limits(sc->enp, &limits); if ((rc = efx_nic_init(sc->enp)) != 0) return (rc); rc = efx_nic_get_vi_pool(sc->enp, &evq_allocated, &rxq_allocated, &txq_allocated); if (rc != 0) { efx_nic_fini(sc->enp); return (rc); } - KASSERT(txq_allocated >= SFXGE_TXQ_NTYPES, - ("txq_allocated < SFXGE_TXQ_NTYPES")); + KASSERT(txq_allocated >= SFXGE_EVQ0_N_TXQ(sc), + ("txq_allocated < %u", SFXGE_EVQ0_N_TXQ(sc))); sc->evq_max = MIN(evq_allocated, evq_max); sc->evq_max = MIN(rxq_allocated, sc->evq_max); - sc->evq_max = MIN(txq_allocated - (SFXGE_TXQ_NTYPES - 1), + sc->evq_max = MIN(txq_allocated - (SFXGE_EVQ0_N_TXQ(sc) - 1), sc->evq_max); KASSERT(sc->evq_max <= evq_max, ("allocated more than maximum requested")); #ifdef RSS if (sc->evq_max < rss_getnumbuckets()) device_printf(sc->dev, "The number of allocated queues (%u) " "is less than the number of RSS buckets (%u); " "performance degradation might be observed", sc->evq_max, rss_getnumbuckets()); #endif /* * NIC is kept initialized in the case of success to be able to * initialize port to find out media types. */ return (0); } static int sfxge_set_drv_limits(struct sfxge_softc *sc) { efx_drv_limits_t limits; memset(&limits, 0, sizeof(limits)); /* Limits are strict since take into account initial estimation */ limits.edl_min_evq_count = limits.edl_max_evq_count = sc->intr.n_alloc; limits.edl_min_txq_count = limits.edl_max_txq_count = - sc->intr.n_alloc + SFXGE_TXQ_NTYPES - 1; + sc->intr.n_alloc + SFXGE_EVQ0_N_TXQ(sc) - 1; limits.edl_min_rxq_count = limits.edl_max_rxq_count = sc->intr.n_alloc; return (efx_nic_set_drv_limits(sc->enp, &limits)); } static int sfxge_start(struct sfxge_softc *sc) { int rc; SFXGE_ADAPTER_LOCK_ASSERT_OWNED(sc); if (sc->init_state == SFXGE_STARTED) return (0); if (sc->init_state != SFXGE_REGISTERED) { rc = EINVAL; goto fail; } /* Set required resource limits */ if ((rc = sfxge_set_drv_limits(sc)) != 0) goto fail; if ((rc = efx_nic_init(sc->enp)) != 0) goto fail; /* Start processing interrupts. */ if ((rc = sfxge_intr_start(sc)) != 0) goto fail2; /* Start processing events. */ if ((rc = sfxge_ev_start(sc)) != 0) goto fail3; /* Fire up the port. */ if ((rc = sfxge_port_start(sc)) != 0) goto fail4; /* Start the receiver side. */ if ((rc = sfxge_rx_start(sc)) != 0) goto fail5; /* Start the transmitter side. */ if ((rc = sfxge_tx_start(sc)) != 0) goto fail6; sc->init_state = SFXGE_STARTED; /* Tell the stack we're running. */ sc->ifnet->if_drv_flags |= IFF_DRV_RUNNING; sc->ifnet->if_drv_flags &= ~IFF_DRV_OACTIVE; return (0); fail6: sfxge_rx_stop(sc); fail5: sfxge_port_stop(sc); fail4: sfxge_ev_stop(sc); fail3: sfxge_intr_stop(sc); fail2: efx_nic_fini(sc->enp); fail: device_printf(sc->dev, "sfxge_start: %d\n", rc); return (rc); } static void sfxge_if_init(void *arg) { struct sfxge_softc *sc; sc = (struct sfxge_softc *)arg; SFXGE_ADAPTER_LOCK(sc); (void)sfxge_start(sc); SFXGE_ADAPTER_UNLOCK(sc); } static void sfxge_stop(struct sfxge_softc *sc) { SFXGE_ADAPTER_LOCK_ASSERT_OWNED(sc); if (sc->init_state != SFXGE_STARTED) return; sc->init_state = SFXGE_REGISTERED; /* Stop the transmitter. */ sfxge_tx_stop(sc); /* Stop the receiver. */ sfxge_rx_stop(sc); /* Stop the port. */ sfxge_port_stop(sc); /* Stop processing events. */ sfxge_ev_stop(sc); /* Stop processing interrupts. */ sfxge_intr_stop(sc); efx_nic_fini(sc->enp); sc->ifnet->if_drv_flags &= ~IFF_DRV_RUNNING; } static int sfxge_vpd_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ioc) { efx_vpd_value_t value; int rc = 0; switch (ioc->u.vpd.op) { case SFXGE_VPD_OP_GET_KEYWORD: value.evv_tag = ioc->u.vpd.tag; value.evv_keyword = ioc->u.vpd.keyword; rc = efx_vpd_get(sc->enp, sc->vpd_data, sc->vpd_size, &value); if (rc != 0) break; ioc->u.vpd.len = MIN(ioc->u.vpd.len, value.evv_length); if (ioc->u.vpd.payload != 0) { rc = copyout(value.evv_value, ioc->u.vpd.payload, ioc->u.vpd.len); } break; case SFXGE_VPD_OP_SET_KEYWORD: if (ioc->u.vpd.len > sizeof(value.evv_value)) return (EINVAL); value.evv_tag = ioc->u.vpd.tag; value.evv_keyword = ioc->u.vpd.keyword; value.evv_length = ioc->u.vpd.len; rc = copyin(ioc->u.vpd.payload, value.evv_value, value.evv_length); if (rc != 0) break; rc = efx_vpd_set(sc->enp, sc->vpd_data, sc->vpd_size, &value); if (rc != 0) break; rc = efx_vpd_verify(sc->enp, sc->vpd_data, sc->vpd_size); if (rc != 0) break; rc = efx_vpd_write(sc->enp, sc->vpd_data, sc->vpd_size); break; default: rc = EOPNOTSUPP; break; } return (rc); } static int sfxge_private_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ioc) { switch (ioc->op) { case SFXGE_MCDI_IOC: return (sfxge_mcdi_ioctl(sc, ioc)); case SFXGE_NVRAM_IOC: return (sfxge_nvram_ioctl(sc, ioc)); case SFXGE_VPD_IOC: return (sfxge_vpd_ioctl(sc, ioc)); default: return (EOPNOTSUPP); } } static int sfxge_if_ioctl(struct ifnet *ifp, unsigned long command, caddr_t data) { struct sfxge_softc *sc; struct ifreq *ifr; sfxge_ioc_t ioc; int error; ifr = (struct ifreq *)data; sc = ifp->if_softc; error = 0; switch (command) { case SIOCSIFFLAGS: SFXGE_ADAPTER_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if ((ifp->if_flags ^ sc->if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) { sfxge_mac_filter_set(sc); } } else sfxge_start(sc); } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) sfxge_stop(sc); sc->if_flags = ifp->if_flags; SFXGE_ADAPTER_UNLOCK(sc); break; case SIOCSIFMTU: if (ifr->ifr_mtu == ifp->if_mtu) { /* Nothing to do */ error = 0; } else if (ifr->ifr_mtu > SFXGE_MAX_MTU) { error = EINVAL; } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { ifp->if_mtu = ifr->ifr_mtu; error = 0; } else { /* Restart required */ SFXGE_ADAPTER_LOCK(sc); sfxge_stop(sc); ifp->if_mtu = ifr->ifr_mtu; error = sfxge_start(sc); SFXGE_ADAPTER_UNLOCK(sc); if (error != 0) { ifp->if_flags &= ~IFF_UP; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; if_down(ifp); } } break; case SIOCADDMULTI: case SIOCDELMULTI: if (ifp->if_drv_flags & IFF_DRV_RUNNING) sfxge_mac_filter_set(sc); break; case SIOCSIFCAP: { int reqcap = ifr->ifr_reqcap; int capchg_mask; SFXGE_ADAPTER_LOCK(sc); /* Capabilities to be changed in accordance with request */ capchg_mask = ifp->if_capenable ^ reqcap; /* * The networking core already rejects attempts to * enable capabilities we don't have. We still have * to reject attempts to disable capabilities that we * can't (yet) disable. */ KASSERT((reqcap & ~ifp->if_capabilities) == 0, ("Unsupported capabilities 0x%x requested 0x%x vs " "supported 0x%x", reqcap & ~ifp->if_capabilities, reqcap , ifp->if_capabilities)); if (capchg_mask & SFXGE_CAP_FIXED) { error = EINVAL; SFXGE_ADAPTER_UNLOCK(sc); break; } /* Check request before any changes */ if ((capchg_mask & IFCAP_TSO4) && (reqcap & (IFCAP_TSO4 | IFCAP_TXCSUM)) == IFCAP_TSO4) { error = EAGAIN; SFXGE_ADAPTER_UNLOCK(sc); if_printf(ifp, "enable txcsum before tso4\n"); break; } if ((capchg_mask & IFCAP_TSO6) && (reqcap & (IFCAP_TSO6 | IFCAP_TXCSUM_IPV6)) == IFCAP_TSO6) { error = EAGAIN; SFXGE_ADAPTER_UNLOCK(sc); if_printf(ifp, "enable txcsum6 before tso6\n"); break; } if (reqcap & IFCAP_TXCSUM) { ifp->if_hwassist |= (CSUM_IP | CSUM_TCP | CSUM_UDP); } else { ifp->if_hwassist &= ~(CSUM_IP | CSUM_TCP | CSUM_UDP); if (reqcap & IFCAP_TSO4) { reqcap &= ~IFCAP_TSO4; if_printf(ifp, "tso4 disabled due to -txcsum\n"); } } if (reqcap & IFCAP_TXCSUM_IPV6) { ifp->if_hwassist |= (CSUM_TCP_IPV6 | CSUM_UDP_IPV6); } else { ifp->if_hwassist &= ~(CSUM_TCP_IPV6 | CSUM_UDP_IPV6); if (reqcap & IFCAP_TSO6) { reqcap &= ~IFCAP_TSO6; if_printf(ifp, "tso6 disabled due to -txcsum6\n"); } } /* * The kernel takes both IFCAP_TSOx and CSUM_TSO into * account before using TSO. So, we do not touch * checksum flags when IFCAP_TSOx is modified. * Note that CSUM_TSO is (CSUM_IP_TSO|CSUM_IP6_TSO), * but both bits are set in IPv4 and IPv6 mbufs. */ ifp->if_capenable = reqcap; SFXGE_ADAPTER_UNLOCK(sc); break; } case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->media, command); break; #ifdef SIOCGI2C case SIOCGI2C: { struct ifi2creq i2c; error = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c)); if (error != 0) break; if (i2c.len > sizeof(i2c.data)) { error = EINVAL; break; } SFXGE_ADAPTER_LOCK(sc); error = efx_phy_module_get_info(sc->enp, i2c.dev_addr, i2c.offset, i2c.len, &i2c.data[0]); SFXGE_ADAPTER_UNLOCK(sc); if (error == 0) error = copyout(&i2c, ifr_data_get_ptr(ifr), sizeof(i2c)); break; } #endif case SIOCGPRIVATE_0: error = priv_check(curthread, PRIV_DRIVER); if (error != 0) break; error = copyin(ifr_data_get_ptr(ifr), &ioc, sizeof(ioc)); if (error != 0) return (error); error = sfxge_private_ioctl(sc, &ioc); if (error == 0) { error = copyout(&ioc, ifr_data_get_ptr(ifr), sizeof(ioc)); } break; default: error = ether_ioctl(ifp, command, data); } return (error); } static void sfxge_ifnet_fini(struct ifnet *ifp) { struct sfxge_softc *sc = ifp->if_softc; SFXGE_ADAPTER_LOCK(sc); sfxge_stop(sc); SFXGE_ADAPTER_UNLOCK(sc); ifmedia_removeall(&sc->media); ether_ifdetach(ifp); if_free(ifp); } static int sfxge_ifnet_init(struct ifnet *ifp, struct sfxge_softc *sc) { const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp); device_t dev; int rc; dev = sc->dev; sc->ifnet = ifp; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_init = sfxge_if_init; ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = sfxge_if_ioctl; ifp->if_capabilities = SFXGE_CAP; ifp->if_capenable = SFXGE_CAP_ENABLE; ifp->if_hw_tsomax = SFXGE_TSO_MAX_SIZE; ifp->if_hw_tsomaxsegcount = SFXGE_TX_MAPPING_MAX_SEG; ifp->if_hw_tsomaxsegsize = PAGE_SIZE; #ifdef SFXGE_LRO ifp->if_capabilities |= IFCAP_LRO; ifp->if_capenable |= IFCAP_LRO; #endif if (encp->enc_hw_tx_insert_vlan_enabled) { ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; ifp->if_capenable |= IFCAP_VLAN_HWTAGGING; } ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_IP | CSUM_TSO | CSUM_TCP_IPV6 | CSUM_UDP_IPV6; ether_ifattach(ifp, encp->enc_mac_addr); ifp->if_transmit = sfxge_if_transmit; ifp->if_qflush = sfxge_if_qflush; ifp->if_get_counter = sfxge_get_counter; DBGPRINT(sc->dev, "ifmedia_init"); if ((rc = sfxge_port_ifmedia_init(sc)) != 0) goto fail; return (0); fail: ether_ifdetach(sc->ifnet); return (rc); } void sfxge_sram_buf_tbl_alloc(struct sfxge_softc *sc, size_t n, uint32_t *idp) { KASSERT(sc->buffer_table_next + n <= efx_nic_cfg_get(sc->enp)->enc_buftbl_limit, ("buffer table full")); *idp = sc->buffer_table_next; sc->buffer_table_next += n; } static int sfxge_bar_init(struct sfxge_softc *sc) { efsys_bar_t *esbp = &sc->bar; esbp->esb_rid = PCIR_BAR(sc->mem_bar); if ((esbp->esb_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &esbp->esb_rid, RF_ACTIVE)) == NULL) { device_printf(sc->dev, "Cannot allocate BAR region %d\n", sc->mem_bar); return (ENXIO); } esbp->esb_tag = rman_get_bustag(esbp->esb_res); esbp->esb_handle = rman_get_bushandle(esbp->esb_res); SFXGE_BAR_LOCK_INIT(esbp, device_get_nameunit(sc->dev)); return (0); } static void sfxge_bar_fini(struct sfxge_softc *sc) { efsys_bar_t *esbp = &sc->bar; bus_release_resource(sc->dev, SYS_RES_MEMORY, esbp->esb_rid, esbp->esb_res); SFXGE_BAR_LOCK_DESTROY(esbp); } static int sfxge_create(struct sfxge_softc *sc) { device_t dev; efx_nic_t *enp; int error; char rss_param_name[sizeof(SFXGE_PARAM(%d.max_rss_channels))]; #if EFSYS_OPT_MCDI_LOGGING char mcdi_log_param_name[sizeof(SFXGE_PARAM(%d.mcdi_logging))]; #endif dev = sc->dev; SFXGE_ADAPTER_LOCK_INIT(sc, device_get_nameunit(sc->dev)); sc->max_rss_channels = 0; snprintf(rss_param_name, sizeof(rss_param_name), SFXGE_PARAM(%d.max_rss_channels), (int)device_get_unit(dev)); TUNABLE_INT_FETCH(rss_param_name, &sc->max_rss_channels); #if EFSYS_OPT_MCDI_LOGGING sc->mcdi_logging = sfxge_mcdi_logging; snprintf(mcdi_log_param_name, sizeof(mcdi_log_param_name), SFXGE_PARAM(%d.mcdi_logging), (int)device_get_unit(dev)); TUNABLE_INT_FETCH(mcdi_log_param_name, &sc->mcdi_logging); #endif sc->stats_node = SYSCTL_ADD_NODE( device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "stats", CTLFLAG_RD, NULL, "Statistics"); if (sc->stats_node == NULL) { error = ENOMEM; goto fail; } TASK_INIT(&sc->task_reset, 0, sfxge_reset, sc); (void) pci_enable_busmaster(dev); /* Initialize DMA mappings. */ DBGPRINT(sc->dev, "dma_init..."); if ((error = sfxge_dma_init(sc)) != 0) goto fail; error = efx_family(pci_get_vendor(dev), pci_get_device(dev), &sc->family, &sc->mem_bar); KASSERT(error == 0, ("Family should be filtered by sfxge_probe()")); /* Map the device registers. */ DBGPRINT(sc->dev, "bar_init..."); if ((error = sfxge_bar_init(sc)) != 0) goto fail; DBGPRINT(sc->dev, "nic_create..."); /* Create the common code nic object. */ SFXGE_EFSYS_LOCK_INIT(&sc->enp_lock, device_get_nameunit(sc->dev), "nic"); if ((error = efx_nic_create(sc->family, (efsys_identifier_t *)sc, &sc->bar, &sc->enp_lock, &enp)) != 0) goto fail3; sc->enp = enp; /* Initialize MCDI to talk to the microcontroller. */ DBGPRINT(sc->dev, "mcdi_init..."); if ((error = sfxge_mcdi_init(sc)) != 0) goto fail4; /* Probe the NIC and build the configuration data area. */ DBGPRINT(sc->dev, "nic_probe..."); if ((error = efx_nic_probe(enp, EFX_FW_VARIANT_DONT_CARE)) != 0) goto fail5; if (!ISP2(sfxge_rx_ring_entries) || (sfxge_rx_ring_entries < EFX_RXQ_MINNDESCS) || (sfxge_rx_ring_entries > EFX_RXQ_MAXNDESCS)) { log(LOG_ERR, "%s=%d must be power of 2 from %u to %u", SFXGE_PARAM_RX_RING, sfxge_rx_ring_entries, EFX_RXQ_MINNDESCS, EFX_RXQ_MAXNDESCS); error = EINVAL; goto fail_rx_ring_entries; } sc->rxq_entries = sfxge_rx_ring_entries; + + if (efx_nic_cfg_get(enp)->enc_features & EFX_FEATURE_TXQ_CKSUM_OP_DESC) + sc->txq_dynamic_cksum_toggle_supported = B_TRUE; + else + sc->txq_dynamic_cksum_toggle_supported = B_FALSE; if (!ISP2(sfxge_tx_ring_entries) || (sfxge_tx_ring_entries < EFX_TXQ_MINNDESCS) || (sfxge_tx_ring_entries > efx_nic_cfg_get(enp)->enc_txq_max_ndescs)) { log(LOG_ERR, "%s=%d must be power of 2 from %u to %u", SFXGE_PARAM_TX_RING, sfxge_tx_ring_entries, EFX_TXQ_MINNDESCS, efx_nic_cfg_get(enp)->enc_txq_max_ndescs); error = EINVAL; goto fail_tx_ring_entries; } sc->txq_entries = sfxge_tx_ring_entries; SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "version", CTLFLAG_RD, SFXGE_VERSION_STRING, 0, "Driver version"); SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "phy_type", CTLFLAG_RD, NULL, efx_nic_cfg_get(enp)->enc_phy_type, "PHY type"); /* Initialize the NVRAM. */ DBGPRINT(sc->dev, "nvram_init..."); if ((error = efx_nvram_init(enp)) != 0) goto fail6; /* Initialize the VPD. */ DBGPRINT(sc->dev, "vpd_init..."); if ((error = efx_vpd_init(enp)) != 0) goto fail7; efx_mcdi_new_epoch(enp); /* Reset the NIC. */ DBGPRINT(sc->dev, "nic_reset..."); if ((error = efx_nic_reset(enp)) != 0) goto fail8; /* Initialize buffer table allocation. */ sc->buffer_table_next = 0; /* * Guarantee minimum and estimate maximum number of event queues * to take it into account when MSI-X interrupts are allocated. * It initializes NIC and keeps it initialized on success. */ if ((error = sfxge_estimate_rsrc_limits(sc)) != 0) goto fail8; /* Set up interrupts. */ DBGPRINT(sc->dev, "intr_init..."); if ((error = sfxge_intr_init(sc)) != 0) goto fail9; /* Initialize event processing state. */ DBGPRINT(sc->dev, "ev_init..."); if ((error = sfxge_ev_init(sc)) != 0) goto fail11; /* Initialize port state. */ DBGPRINT(sc->dev, "port_init..."); if ((error = sfxge_port_init(sc)) != 0) goto fail12; /* Initialize receive state. */ DBGPRINT(sc->dev, "rx_init..."); if ((error = sfxge_rx_init(sc)) != 0) goto fail13; /* Initialize transmit state. */ DBGPRINT(sc->dev, "tx_init..."); if ((error = sfxge_tx_init(sc)) != 0) goto fail14; sc->init_state = SFXGE_INITIALIZED; DBGPRINT(sc->dev, "success"); return (0); fail14: sfxge_rx_fini(sc); fail13: sfxge_port_fini(sc); fail12: sfxge_ev_fini(sc); fail11: sfxge_intr_fini(sc); fail9: efx_nic_fini(sc->enp); fail8: efx_vpd_fini(enp); fail7: efx_nvram_fini(enp); fail6: fail_tx_ring_entries: fail_rx_ring_entries: efx_nic_unprobe(enp); fail5: sfxge_mcdi_fini(sc); fail4: sc->enp = NULL; efx_nic_destroy(enp); SFXGE_EFSYS_LOCK_DESTROY(&sc->enp_lock); fail3: sfxge_bar_fini(sc); (void) pci_disable_busmaster(sc->dev); fail: DBGPRINT(sc->dev, "failed %d", error); sc->dev = NULL; SFXGE_ADAPTER_LOCK_DESTROY(sc); return (error); } static void sfxge_destroy(struct sfxge_softc *sc) { efx_nic_t *enp; /* Clean up transmit state. */ sfxge_tx_fini(sc); /* Clean up receive state. */ sfxge_rx_fini(sc); /* Clean up port state. */ sfxge_port_fini(sc); /* Clean up event processing state. */ sfxge_ev_fini(sc); /* Clean up interrupts. */ sfxge_intr_fini(sc); /* Tear down common code subsystems. */ efx_nic_reset(sc->enp); efx_vpd_fini(sc->enp); efx_nvram_fini(sc->enp); efx_nic_unprobe(sc->enp); /* Tear down MCDI. */ sfxge_mcdi_fini(sc); /* Destroy common code context. */ enp = sc->enp; sc->enp = NULL; efx_nic_destroy(enp); /* Free DMA memory. */ sfxge_dma_fini(sc); /* Free mapped BARs. */ sfxge_bar_fini(sc); (void) pci_disable_busmaster(sc->dev); taskqueue_drain(taskqueue_thread, &sc->task_reset); /* Destroy the softc lock. */ SFXGE_ADAPTER_LOCK_DESTROY(sc); } static int sfxge_vpd_handler(SYSCTL_HANDLER_ARGS) { struct sfxge_softc *sc = arg1; efx_vpd_value_t value; int rc; value.evv_tag = arg2 >> 16; value.evv_keyword = arg2 & 0xffff; if ((rc = efx_vpd_get(sc->enp, sc->vpd_data, sc->vpd_size, &value)) != 0) return (rc); return (SYSCTL_OUT(req, value.evv_value, value.evv_length)); } static void sfxge_vpd_try_add(struct sfxge_softc *sc, struct sysctl_oid_list *list, efx_vpd_tag_t tag, const char *keyword) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); efx_vpd_value_t value; /* Check whether VPD tag/keyword is present */ value.evv_tag = tag; value.evv_keyword = EFX_VPD_KEYWORD(keyword[0], keyword[1]); if (efx_vpd_get(sc->enp, sc->vpd_data, sc->vpd_size, &value) != 0) return; SYSCTL_ADD_PROC( ctx, list, OID_AUTO, keyword, CTLTYPE_STRING|CTLFLAG_RD, sc, tag << 16 | EFX_VPD_KEYWORD(keyword[0], keyword[1]), sfxge_vpd_handler, "A", ""); } static int sfxge_vpd_init(struct sfxge_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); struct sysctl_oid *vpd_node; struct sysctl_oid_list *vpd_list; char keyword[3]; efx_vpd_value_t value; int rc; if ((rc = efx_vpd_size(sc->enp, &sc->vpd_size)) != 0) { /* * Unpriviledged functions deny VPD access. * Simply skip VPD in this case. */ if (rc == EACCES) goto done; goto fail; } sc->vpd_data = malloc(sc->vpd_size, M_SFXGE, M_WAITOK); if ((rc = efx_vpd_read(sc->enp, sc->vpd_data, sc->vpd_size)) != 0) goto fail2; /* Copy ID (product name) into device description, and log it. */ value.evv_tag = EFX_VPD_ID; if (efx_vpd_get(sc->enp, sc->vpd_data, sc->vpd_size, &value) == 0) { value.evv_value[value.evv_length] = 0; device_set_desc_copy(sc->dev, value.evv_value); device_printf(sc->dev, "%s\n", value.evv_value); } vpd_node = SYSCTL_ADD_NODE( ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "vpd", CTLFLAG_RD, NULL, "Vital Product Data"); vpd_list = SYSCTL_CHILDREN(vpd_node); /* Add sysctls for all expected and any vendor-defined keywords. */ sfxge_vpd_try_add(sc, vpd_list, EFX_VPD_RO, "PN"); sfxge_vpd_try_add(sc, vpd_list, EFX_VPD_RO, "EC"); sfxge_vpd_try_add(sc, vpd_list, EFX_VPD_RO, "SN"); keyword[0] = 'V'; keyword[2] = 0; for (keyword[1] = '0'; keyword[1] <= '9'; keyword[1]++) sfxge_vpd_try_add(sc, vpd_list, EFX_VPD_RO, keyword); for (keyword[1] = 'A'; keyword[1] <= 'Z'; keyword[1]++) sfxge_vpd_try_add(sc, vpd_list, EFX_VPD_RO, keyword); done: return (0); fail2: free(sc->vpd_data, M_SFXGE); fail: return (rc); } static void sfxge_vpd_fini(struct sfxge_softc *sc) { free(sc->vpd_data, M_SFXGE); } static void sfxge_reset(void *arg, int npending) { struct sfxge_softc *sc; int rc; unsigned attempt; (void)npending; sc = (struct sfxge_softc *)arg; SFXGE_ADAPTER_LOCK(sc); if (sc->init_state != SFXGE_STARTED) goto done; sfxge_stop(sc); efx_nic_reset(sc->enp); for (attempt = 0; attempt < sfxge_restart_attempts; ++attempt) { if ((rc = sfxge_start(sc)) == 0) goto done; device_printf(sc->dev, "start on reset failed (%d)\n", rc); DELAY(100000); } device_printf(sc->dev, "reset failed; interface is now stopped\n"); done: SFXGE_ADAPTER_UNLOCK(sc); } void sfxge_schedule_reset(struct sfxge_softc *sc) { taskqueue_enqueue(taskqueue_thread, &sc->task_reset); } static int sfxge_attach(device_t dev) { struct sfxge_softc *sc; struct ifnet *ifp; int error; sc = device_get_softc(dev); sc->dev = dev; /* Allocate ifnet. */ ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "Couldn't allocate ifnet\n"); error = ENOMEM; goto fail; } sc->ifnet = ifp; /* Initialize hardware. */ DBGPRINT(sc->dev, "create nic"); if ((error = sfxge_create(sc)) != 0) goto fail2; /* Create the ifnet for the port. */ DBGPRINT(sc->dev, "init ifnet"); if ((error = sfxge_ifnet_init(ifp, sc)) != 0) goto fail3; DBGPRINT(sc->dev, "init vpd"); if ((error = sfxge_vpd_init(sc)) != 0) goto fail4; /* * NIC is initialized inside sfxge_create() and kept inialized * to be able to initialize port to discover media types in * sfxge_ifnet_init(). */ efx_nic_fini(sc->enp); sc->init_state = SFXGE_REGISTERED; DBGPRINT(sc->dev, "success"); return (0); fail4: sfxge_ifnet_fini(ifp); fail3: efx_nic_fini(sc->enp); sfxge_destroy(sc); fail2: if_free(sc->ifnet); fail: DBGPRINT(sc->dev, "failed %d", error); return (error); } static int sfxge_detach(device_t dev) { struct sfxge_softc *sc; sc = device_get_softc(dev); sfxge_vpd_fini(sc); /* Destroy the ifnet. */ sfxge_ifnet_fini(sc->ifnet); /* Tear down hardware. */ sfxge_destroy(sc); return (0); } static int sfxge_probe(device_t dev) { uint16_t pci_vendor_id; uint16_t pci_device_id; efx_family_t family; unsigned int mem_bar; int rc; pci_vendor_id = pci_get_vendor(dev); pci_device_id = pci_get_device(dev); DBGPRINT(dev, "PCI ID %04x:%04x", pci_vendor_id, pci_device_id); rc = efx_family(pci_vendor_id, pci_device_id, &family, &mem_bar); if (rc != 0) { DBGPRINT(dev, "efx_family fail %d", rc); return (ENXIO); } if (family == EFX_FAMILY_SIENA) { device_set_desc(dev, "Solarflare SFC9000 family"); return (0); } if (family == EFX_FAMILY_HUNTINGTON) { device_set_desc(dev, "Solarflare SFC9100 family"); return (0); } if (family == EFX_FAMILY_MEDFORD) { device_set_desc(dev, "Solarflare SFC9200 family"); return (0); } if (family == EFX_FAMILY_MEDFORD2) { device_set_desc(dev, "Solarflare SFC9250 family"); return (0); } DBGPRINT(dev, "impossible controller family %d", family); return (ENXIO); } static device_method_t sfxge_methods[] = { DEVMETHOD(device_probe, sfxge_probe), DEVMETHOD(device_attach, sfxge_attach), DEVMETHOD(device_detach, sfxge_detach), DEVMETHOD_END }; static devclass_t sfxge_devclass; static driver_t sfxge_driver = { "sfxge", sfxge_methods, sizeof(struct sfxge_softc) }; DRIVER_MODULE(sfxge, pci, sfxge_driver, sfxge_devclass, 0, 0); Index: projects/clang700-import/sys/dev/sfxge/sfxge.h =================================================================== --- projects/clang700-import/sys/dev/sfxge/sfxge.h (revision 341812) +++ projects/clang700-import/sys/dev/sfxge/sfxge.h (revision 341813) @@ -1,483 +1,492 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010-2016 Solarflare Communications Inc. * All rights reserved. * * This software was developed in part by Philip Paeps under contract for * Solarflare Communications, 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. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the FreeBSD Project. * * $FreeBSD$ */ #ifndef _SFXGE_H #define _SFXGE_H #include #include #include #include #include #include #include #include #include #include #include #include "sfxge_ioc.h" /* * Debugging */ #if 0 #define DBGPRINT(dev, fmt, args...) \ device_printf(dev, "%s: " fmt "\n", __func__, ## args) #else #define DBGPRINT(dev, fmt, args...) #endif /* * Backward-compatibility */ #ifndef CACHE_LINE_SIZE /* This should be right on most machines the driver will be used on, and * we needn't care too much about wasting a few KB per interface. */ #define CACHE_LINE_SIZE 128 #endif #ifndef IFCAP_LINKSTATE #define IFCAP_LINKSTATE 0 #endif #ifndef IFCAP_VLAN_HWTSO #define IFCAP_VLAN_HWTSO 0 #endif #ifndef IFM_10G_T #define IFM_10G_T IFM_UNKNOWN #endif #ifndef IFM_10G_KX4 #define IFM_10G_KX4 IFM_10G_CX4 #endif #ifndef IFM_40G_CR4 #define IFM_40G_CR4 IFM_UNKNOWN #endif #if (__FreeBSD_version >= 800501 && __FreeBSD_version < 900000) || \ __FreeBSD_version >= 900003 #define SFXGE_HAVE_DESCRIBE_INTR #endif #ifdef IFM_ETH_RXPAUSE #define SFXGE_HAVE_PAUSE_MEDIAOPTS #endif #ifndef CTLTYPE_U64 #define CTLTYPE_U64 CTLTYPE_QUAD #endif #include "sfxge_rx.h" #include "sfxge_tx.h" #define ROUNDUP_POW_OF_TWO(_n) (1ULL << flsl((_n) - 1)) #define SFXGE_IP_ALIGN 2 #define SFXGE_ETHERTYPE_LOOPBACK 0x9000 /* Xerox loopback */ #define SFXGE_MAGIC_RESERVED 0x8000 #define SFXGE_MAGIC_DMAQ_LABEL_WIDTH 6 #define SFXGE_MAGIC_DMAQ_LABEL_MASK \ ((1 << SFXGE_MAGIC_DMAQ_LABEL_WIDTH) - 1) enum sfxge_sw_ev { SFXGE_SW_EV_RX_QFLUSH_DONE = 1, SFXGE_SW_EV_RX_QFLUSH_FAILED, SFXGE_SW_EV_RX_QREFILL, SFXGE_SW_EV_TX_QFLUSH_DONE, }; #define SFXGE_SW_EV_MAGIC(_sw_ev) \ (SFXGE_MAGIC_RESERVED | ((_sw_ev) << SFXGE_MAGIC_DMAQ_LABEL_WIDTH)) static inline uint16_t sfxge_sw_ev_mk_magic(enum sfxge_sw_ev sw_ev, unsigned int label) { KASSERT((label & SFXGE_MAGIC_DMAQ_LABEL_MASK) == label, ("(label & SFXGE_MAGIC_DMAQ_LABEL_MASK) != label")); return SFXGE_SW_EV_MAGIC(sw_ev) | label; } static inline uint16_t sfxge_sw_ev_rxq_magic(enum sfxge_sw_ev sw_ev, struct sfxge_rxq *rxq) { return sfxge_sw_ev_mk_magic(sw_ev, 0); } static inline uint16_t sfxge_sw_ev_txq_magic(enum sfxge_sw_ev sw_ev, struct sfxge_txq *txq) { return sfxge_sw_ev_mk_magic(sw_ev, txq->type); } enum sfxge_evq_state { SFXGE_EVQ_UNINITIALIZED = 0, SFXGE_EVQ_INITIALIZED, SFXGE_EVQ_STARTING, SFXGE_EVQ_STARTED }; #define SFXGE_EV_BATCH 16384 #define SFXGE_STATS_UPDATE_PERIOD_MS 1000 struct sfxge_evq { /* Structure members below are sorted by usage order */ struct sfxge_softc *sc; struct mtx lock; unsigned int index; enum sfxge_evq_state init_state; efsys_mem_t mem; efx_evq_t *common; unsigned int read_ptr; boolean_t exception; unsigned int rx_done; unsigned int tx_done; /* Linked list of TX queues with completions to process */ struct sfxge_txq *txq; struct sfxge_txq **txqs; /* Structure members not used on event processing path */ unsigned int buf_base_id; unsigned int entries; char lock_name[SFXGE_LOCK_NAME_MAX]; +#if EFSYS_OPT_QSTATS + clock_t stats_update_time; + uint64_t stats[EV_NQSTATS]; +#endif } __aligned(CACHE_LINE_SIZE); #define SFXGE_NDESCS 1024 #define SFXGE_MODERATION 30 enum sfxge_intr_state { SFXGE_INTR_UNINITIALIZED = 0, SFXGE_INTR_INITIALIZED, SFXGE_INTR_TESTING, SFXGE_INTR_STARTED }; struct sfxge_intr_hdl { int eih_rid; void *eih_tag; struct resource *eih_res; }; struct sfxge_intr { enum sfxge_intr_state state; struct resource *msix_res; struct sfxge_intr_hdl *table; int n_alloc; int type; efsys_mem_t status; uint32_t zero_count; }; enum sfxge_mcdi_state { SFXGE_MCDI_UNINITIALIZED = 0, SFXGE_MCDI_INITIALIZED, SFXGE_MCDI_BUSY, SFXGE_MCDI_COMPLETED }; struct sfxge_mcdi { struct mtx lock; efsys_mem_t mem; enum sfxge_mcdi_state state; efx_mcdi_transport_t transport; /* Only used in debugging output */ char lock_name[SFXGE_LOCK_NAME_MAX]; }; struct sfxge_hw_stats { clock_t update_time; efsys_mem_t dma_buf; void *decode_buf; }; enum sfxge_port_state { SFXGE_PORT_UNINITIALIZED = 0, SFXGE_PORT_INITIALIZED, SFXGE_PORT_STARTED }; struct sfxge_port { struct sfxge_softc *sc; struct mtx lock; enum sfxge_port_state init_state; #ifndef SFXGE_HAVE_PAUSE_MEDIAOPTS unsigned int wanted_fc; #endif struct sfxge_hw_stats phy_stats; struct sfxge_hw_stats mac_stats; uint16_t stats_update_period_ms; efx_link_mode_t link_mode; uint8_t mcast_addrs[EFX_MAC_MULTICAST_LIST_MAX * EFX_MAC_ADDR_LEN]; unsigned int mcast_count; /* Only used in debugging output */ char lock_name[SFXGE_LOCK_NAME_MAX]; }; enum sfxge_softc_state { SFXGE_UNINITIALIZED = 0, SFXGE_INITIALIZED, SFXGE_REGISTERED, SFXGE_STARTED }; struct sfxge_softc { device_t dev; struct sx softc_lock; char softc_lock_name[SFXGE_LOCK_NAME_MAX]; enum sfxge_softc_state init_state; struct ifnet *ifnet; unsigned int if_flags; struct sysctl_oid *stats_node; +#if EFSYS_OPT_QSTATS + struct sysctl_oid *evqs_stats_node; +#endif struct sysctl_oid *txqs_node; struct task task_reset; efx_family_t family; unsigned int mem_bar; caddr_t vpd_data; size_t vpd_size; efx_nic_t *enp; efsys_lock_t enp_lock; + + boolean_t txq_dynamic_cksum_toggle_supported; unsigned int rxq_entries; unsigned int txq_entries; bus_dma_tag_t parent_dma_tag; efsys_bar_t bar; struct sfxge_intr intr; struct sfxge_mcdi mcdi; struct sfxge_port port; uint32_t buffer_table_next; struct sfxge_evq *evq[SFXGE_RX_SCALE_MAX]; unsigned int ev_moderation; #if EFSYS_OPT_QSTATS clock_t ev_stats_update_time; uint64_t ev_stats[EV_NQSTATS]; #endif unsigned int max_rss_channels; struct sfxge_rxq *rxq[SFXGE_RX_SCALE_MAX]; unsigned int rx_indir_table[EFX_RSS_TBL_SIZE]; struct sfxge_txq *txq[SFXGE_TXQ_NTYPES + SFXGE_RX_SCALE_MAX]; struct ifmedia media; size_t rx_prefix_size; size_t rx_buffer_size; size_t rx_buffer_align; int rx_cluster_size; unsigned int evq_max; unsigned int evq_count; unsigned int rxq_count; unsigned int txq_count; unsigned int tso_fw_assisted; #define SFXGE_FATSOV1 (1 << 0) #define SFXGE_FATSOV2 (1 << 1) #if EFSYS_OPT_MCDI_LOGGING int mcdi_logging; #endif }; #define SFXGE_LINK_UP(sc) \ ((sc)->port.link_mode != EFX_LINK_DOWN && \ (sc)->port.link_mode != EFX_LINK_UNKNOWN) #define SFXGE_RUNNING(sc) ((sc)->ifnet->if_drv_flags & IFF_DRV_RUNNING) #define SFXGE_PARAM(_name) "hw.sfxge." #_name SYSCTL_DECL(_hw_sfxge); /* * From sfxge.c. */ extern void sfxge_schedule_reset(struct sfxge_softc *sc); extern void sfxge_sram_buf_tbl_alloc(struct sfxge_softc *sc, size_t n, uint32_t *idp); /* * From sfxge_dma.c. */ extern int sfxge_dma_init(struct sfxge_softc *sc); extern void sfxge_dma_fini(struct sfxge_softc *sc); extern int sfxge_dma_alloc(struct sfxge_softc *sc, bus_size_t len, efsys_mem_t *esmp); extern void sfxge_dma_free(efsys_mem_t *esmp); extern int sfxge_dma_map_sg_collapse(bus_dma_tag_t tag, bus_dmamap_t map, struct mbuf **mp, bus_dma_segment_t *segs, int *nsegs, int maxsegs); /* * From sfxge_ev.c. */ extern int sfxge_ev_init(struct sfxge_softc *sc); extern void sfxge_ev_fini(struct sfxge_softc *sc); extern int sfxge_ev_start(struct sfxge_softc *sc); extern void sfxge_ev_stop(struct sfxge_softc *sc); extern int sfxge_ev_qpoll(struct sfxge_evq *evq); /* * From sfxge_intr.c. */ extern int sfxge_intr_init(struct sfxge_softc *sc); extern void sfxge_intr_fini(struct sfxge_softc *sc); extern int sfxge_intr_start(struct sfxge_softc *sc); extern void sfxge_intr_stop(struct sfxge_softc *sc); /* * From sfxge_mcdi.c. */ extern int sfxge_mcdi_init(struct sfxge_softc *sc); extern void sfxge_mcdi_fini(struct sfxge_softc *sc); extern int sfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip); /* * From sfxge_nvram.c. */ extern int sfxge_nvram_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip); /* * From sfxge_port.c. */ extern int sfxge_port_init(struct sfxge_softc *sc); extern void sfxge_port_fini(struct sfxge_softc *sc); extern int sfxge_port_start(struct sfxge_softc *sc); extern void sfxge_port_stop(struct sfxge_softc *sc); extern void sfxge_mac_link_update(struct sfxge_softc *sc, efx_link_mode_t mode); extern int sfxge_mac_filter_set(struct sfxge_softc *sc); extern int sfxge_port_ifmedia_init(struct sfxge_softc *sc); extern uint64_t sfxge_get_counter(struct ifnet *ifp, ift_counter c); #define SFXGE_MAX_MTU (9 * 1024) #define SFXGE_ADAPTER_LOCK_INIT(_sc, _ifname) \ do { \ struct sfxge_softc *__sc = (_sc); \ \ snprintf((__sc)->softc_lock_name, \ sizeof((__sc)->softc_lock_name), \ "%s:softc", (_ifname)); \ sx_init(&(__sc)->softc_lock, (__sc)->softc_lock_name); \ } while (B_FALSE) #define SFXGE_ADAPTER_LOCK_DESTROY(_sc) \ sx_destroy(&(_sc)->softc_lock) #define SFXGE_ADAPTER_LOCK(_sc) \ sx_xlock(&(_sc)->softc_lock) #define SFXGE_ADAPTER_UNLOCK(_sc) \ sx_xunlock(&(_sc)->softc_lock) #define SFXGE_ADAPTER_LOCK_ASSERT_OWNED(_sc) \ sx_assert(&(_sc)->softc_lock, LA_XLOCKED) #define SFXGE_PORT_LOCK_INIT(_port, _ifname) \ do { \ struct sfxge_port *__port = (_port); \ \ snprintf((__port)->lock_name, \ sizeof((__port)->lock_name), \ "%s:port", (_ifname)); \ mtx_init(&(__port)->lock, (__port)->lock_name, \ NULL, MTX_DEF); \ } while (B_FALSE) #define SFXGE_PORT_LOCK_DESTROY(_port) \ mtx_destroy(&(_port)->lock) #define SFXGE_PORT_LOCK(_port) \ mtx_lock(&(_port)->lock) #define SFXGE_PORT_UNLOCK(_port) \ mtx_unlock(&(_port)->lock) #define SFXGE_PORT_LOCK_ASSERT_OWNED(_port) \ mtx_assert(&(_port)->lock, MA_OWNED) #define SFXGE_MCDI_LOCK_INIT(_mcdi, _ifname) \ do { \ struct sfxge_mcdi *__mcdi = (_mcdi); \ \ snprintf((__mcdi)->lock_name, \ sizeof((__mcdi)->lock_name), \ "%s:mcdi", (_ifname)); \ mtx_init(&(__mcdi)->lock, (__mcdi)->lock_name, \ NULL, MTX_DEF); \ } while (B_FALSE) #define SFXGE_MCDI_LOCK_DESTROY(_mcdi) \ mtx_destroy(&(_mcdi)->lock) #define SFXGE_MCDI_LOCK(_mcdi) \ mtx_lock(&(_mcdi)->lock) #define SFXGE_MCDI_UNLOCK(_mcdi) \ mtx_unlock(&(_mcdi)->lock) #define SFXGE_MCDI_LOCK_ASSERT_OWNED(_mcdi) \ mtx_assert(&(_mcdi)->lock, MA_OWNED) #define SFXGE_EVQ_LOCK_INIT(_evq, _ifname, _evq_index) \ do { \ struct sfxge_evq *__evq = (_evq); \ \ snprintf((__evq)->lock_name, \ sizeof((__evq)->lock_name), \ "%s:evq%u", (_ifname), (_evq_index)); \ mtx_init(&(__evq)->lock, (__evq)->lock_name, \ NULL, MTX_DEF); \ } while (B_FALSE) #define SFXGE_EVQ_LOCK_DESTROY(_evq) \ mtx_destroy(&(_evq)->lock) #define SFXGE_EVQ_LOCK(_evq) \ mtx_lock(&(_evq)->lock) #define SFXGE_EVQ_UNLOCK(_evq) \ mtx_unlock(&(_evq)->lock) #define SFXGE_EVQ_LOCK_ASSERT_OWNED(_evq) \ mtx_assert(&(_evq)->lock, MA_OWNED) #endif /* _SFXGE_H */ Index: projects/clang700-import/sys/dev/sfxge/sfxge_ev.c =================================================================== --- projects/clang700-import/sys/dev/sfxge/sfxge_ev.c (revision 341812) +++ projects/clang700-import/sys/dev/sfxge/sfxge_ev.c (revision 341813) @@ -1,945 +1,1044 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010-2016 Solarflare Communications Inc. * All rights reserved. * * This software was developed in part by Philip Paeps under contract for * Solarflare Communications, 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. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the FreeBSD Project. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "common/efx.h" #include "sfxge.h" static void sfxge_ev_qcomplete(struct sfxge_evq *evq, boolean_t eop) { struct sfxge_softc *sc; unsigned int index; struct sfxge_rxq *rxq; struct sfxge_txq *txq; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); sc = evq->sc; index = evq->index; rxq = sc->rxq[index]; if ((txq = evq->txq) != NULL) { evq->txq = NULL; evq->txqs = &(evq->txq); do { struct sfxge_txq *next; next = txq->next; txq->next = NULL; KASSERT(txq->evq_index == index, ("txq->evq_index != index")); if (txq->pending != txq->completed) sfxge_tx_qcomplete(txq, evq); txq = next; } while (txq != NULL); } if (rxq->pending != rxq->completed) sfxge_rx_qcomplete(rxq, eop); } static struct sfxge_rxq * sfxge_get_rxq_by_label(struct sfxge_evq *evq, uint32_t label) { struct sfxge_rxq *rxq; KASSERT(label == 0, ("unexpected rxq label != 0")); rxq = evq->sc->rxq[evq->index]; KASSERT(rxq != NULL, ("rxq == NULL")); KASSERT(evq->index == rxq->index, ("evq->index != rxq->index")); return (rxq); } static boolean_t sfxge_ev_rx(void *arg, uint32_t label, uint32_t id, uint32_t size, uint16_t flags) { struct sfxge_evq *evq; struct sfxge_softc *sc; struct sfxge_rxq *rxq; unsigned int stop; unsigned int delta; struct sfxge_rx_sw_desc *rx_desc; evq = arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); sc = evq->sc; if (evq->exception) goto done; rxq = sfxge_get_rxq_by_label(evq, label); if (__predict_false(rxq->init_state != SFXGE_RXQ_STARTED)) goto done; stop = (id + 1) & rxq->ptr_mask; id = rxq->pending & rxq->ptr_mask; delta = (stop >= id) ? (stop - id) : (rxq->entries - id + stop); rxq->pending += delta; if (delta != 1) { if ((delta <= 0) || (delta > efx_nic_cfg_get(sc->enp)->enc_rx_batch_max)) { evq->exception = B_TRUE; device_printf(sc->dev, "RX completion out of order" " (id=%#x delta=%u flags=%#x); resetting\n", id, delta, flags); sfxge_schedule_reset(sc); goto done; } } rx_desc = &rxq->queue[id]; prefetch_read_many(rx_desc->mbuf); for (; id != stop; id = (id + 1) & rxq->ptr_mask) { rx_desc = &rxq->queue[id]; KASSERT(rx_desc->flags == EFX_DISCARD, ("rx_desc->flags != EFX_DISCARD")); rx_desc->flags = flags; KASSERT(size < (1 << 16), ("size > (1 << 16)")); rx_desc->size = (uint16_t)size; } evq->rx_done++; if (rxq->pending - rxq->completed >= SFXGE_RX_BATCH) sfxge_ev_qcomplete(evq, B_FALSE); done: return (evq->rx_done >= SFXGE_EV_BATCH); } static boolean_t sfxge_ev_exception(void *arg, uint32_t code, uint32_t data) { struct sfxge_evq *evq; struct sfxge_softc *sc; evq = (struct sfxge_evq *)arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); sc = evq->sc; DBGPRINT(sc->dev, "[%d] %s", evq->index, (code == EFX_EXCEPTION_RX_RECOVERY) ? "RX_RECOVERY" : (code == EFX_EXCEPTION_RX_DSC_ERROR) ? "RX_DSC_ERROR" : (code == EFX_EXCEPTION_TX_DSC_ERROR) ? "TX_DSC_ERROR" : (code == EFX_EXCEPTION_UNKNOWN_SENSOREVT) ? "UNKNOWN_SENSOREVT" : (code == EFX_EXCEPTION_FWALERT_SRAM) ? "FWALERT_SRAM" : (code == EFX_EXCEPTION_UNKNOWN_FWALERT) ? "UNKNOWN_FWALERT" : (code == EFX_EXCEPTION_RX_ERROR) ? "RX_ERROR" : (code == EFX_EXCEPTION_TX_ERROR) ? "TX_ERROR" : (code == EFX_EXCEPTION_EV_ERROR) ? "EV_ERROR" : "UNKNOWN"); evq->exception = B_TRUE; if (code != EFX_EXCEPTION_UNKNOWN_SENSOREVT) { device_printf(sc->dev, "hardware exception (code=%u); resetting\n", code); sfxge_schedule_reset(sc); } return (B_FALSE); } static boolean_t sfxge_ev_rxq_flush_done(void *arg, uint32_t rxq_index) { struct sfxge_evq *evq; struct sfxge_softc *sc; struct sfxge_rxq *rxq; unsigned int index; uint16_t magic; evq = (struct sfxge_evq *)arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); sc = evq->sc; rxq = sc->rxq[rxq_index]; KASSERT(rxq != NULL, ("rxq == NULL")); /* Resend a software event on the correct queue */ index = rxq->index; if (index == evq->index) { sfxge_rx_qflush_done(rxq); return (B_FALSE); } evq = sc->evq[index]; magic = sfxge_sw_ev_rxq_magic(SFXGE_SW_EV_RX_QFLUSH_DONE, rxq); KASSERT(evq->init_state == SFXGE_EVQ_STARTED, ("evq not started")); efx_ev_qpost(evq->common, magic); return (B_FALSE); } static boolean_t sfxge_ev_rxq_flush_failed(void *arg, uint32_t rxq_index) { struct sfxge_evq *evq; struct sfxge_softc *sc; struct sfxge_rxq *rxq; unsigned int index; uint16_t magic; evq = (struct sfxge_evq *)arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); sc = evq->sc; rxq = sc->rxq[rxq_index]; KASSERT(rxq != NULL, ("rxq == NULL")); /* Resend a software event on the correct queue */ index = rxq->index; evq = sc->evq[index]; magic = sfxge_sw_ev_rxq_magic(SFXGE_SW_EV_RX_QFLUSH_FAILED, rxq); KASSERT(evq->init_state == SFXGE_EVQ_STARTED, ("evq not started")); efx_ev_qpost(evq->common, magic); return (B_FALSE); } static struct sfxge_txq * sfxge_get_txq_by_label(struct sfxge_evq *evq, enum sfxge_txq_type label) { unsigned int index; - KASSERT((evq->index == 0 && label < SFXGE_TXQ_NTYPES) || - (label == SFXGE_TXQ_IP_TCP_UDP_CKSUM), ("unexpected txq label")); - index = (evq->index == 0) ? label : (evq->index - 1 + SFXGE_TXQ_NTYPES); + KASSERT((evq->sc->txq_dynamic_cksum_toggle_supported) ? (label == 0) : + ((evq->index == 0 && label < SFXGE_TXQ_NTYPES) || + (label == SFXGE_TXQ_IP_TCP_UDP_CKSUM)), + ("unexpected txq label")); + + index = (evq->index == 0) ? + label : (evq->index - 1 + SFXGE_EVQ0_N_TXQ(evq->sc)); return (evq->sc->txq[index]); } static boolean_t sfxge_ev_tx(void *arg, uint32_t label, uint32_t id) { struct sfxge_evq *evq; struct sfxge_txq *txq; unsigned int stop; unsigned int delta; evq = (struct sfxge_evq *)arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); txq = sfxge_get_txq_by_label(evq, label); KASSERT(txq != NULL, ("txq == NULL")); KASSERT(evq->index == txq->evq_index, ("evq->index != txq->evq_index")); if (__predict_false(txq->init_state != SFXGE_TXQ_STARTED)) goto done; stop = (id + 1) & txq->ptr_mask; id = txq->pending & txq->ptr_mask; delta = (stop >= id) ? (stop - id) : (txq->entries - id + stop); txq->pending += delta; evq->tx_done++; if (txq->next == NULL && evq->txqs != &(txq->next)) { *(evq->txqs) = txq; evq->txqs = &(txq->next); } if (txq->pending - txq->completed >= SFXGE_TX_BATCH) sfxge_tx_qcomplete(txq, evq); done: return (evq->tx_done >= SFXGE_EV_BATCH); } static boolean_t sfxge_ev_txq_flush_done(void *arg, uint32_t txq_index) { struct sfxge_evq *evq; struct sfxge_softc *sc; struct sfxge_txq *txq; uint16_t magic; evq = (struct sfxge_evq *)arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); sc = evq->sc; txq = sc->txq[txq_index]; KASSERT(txq != NULL, ("txq == NULL")); KASSERT(txq->init_state == SFXGE_TXQ_INITIALIZED, ("txq not initialized")); if (txq->evq_index == evq->index) { sfxge_tx_qflush_done(txq); return (B_FALSE); } /* Resend a software event on the correct queue */ evq = sc->evq[txq->evq_index]; magic = sfxge_sw_ev_txq_magic(SFXGE_SW_EV_TX_QFLUSH_DONE, txq); KASSERT(evq->init_state == SFXGE_EVQ_STARTED, ("evq not started")); efx_ev_qpost(evq->common, magic); return (B_FALSE); } static boolean_t sfxge_ev_software(void *arg, uint16_t magic) { struct sfxge_evq *evq; struct sfxge_softc *sc; unsigned int label; evq = (struct sfxge_evq *)arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); sc = evq->sc; label = magic & SFXGE_MAGIC_DMAQ_LABEL_MASK; magic &= ~SFXGE_MAGIC_DMAQ_LABEL_MASK; switch (magic) { case SFXGE_SW_EV_MAGIC(SFXGE_SW_EV_RX_QFLUSH_DONE): sfxge_rx_qflush_done(sfxge_get_rxq_by_label(evq, label)); break; case SFXGE_SW_EV_MAGIC(SFXGE_SW_EV_RX_QFLUSH_FAILED): sfxge_rx_qflush_failed(sfxge_get_rxq_by_label(evq, label)); break; case SFXGE_SW_EV_MAGIC(SFXGE_SW_EV_RX_QREFILL): sfxge_rx_qrefill(sfxge_get_rxq_by_label(evq, label)); break; case SFXGE_SW_EV_MAGIC(SFXGE_SW_EV_TX_QFLUSH_DONE): { struct sfxge_txq *txq = sfxge_get_txq_by_label(evq, label); KASSERT(txq != NULL, ("txq == NULL")); KASSERT(evq->index == txq->evq_index, ("evq->index != txq->evq_index")); sfxge_tx_qflush_done(txq); break; } default: break; } return (B_FALSE); } static boolean_t sfxge_ev_sram(void *arg, uint32_t code) { (void)arg; (void)code; switch (code) { case EFX_SRAM_UPDATE: EFSYS_PROBE(sram_update); break; case EFX_SRAM_CLEAR: EFSYS_PROBE(sram_clear); break; case EFX_SRAM_ILLEGAL_CLEAR: EFSYS_PROBE(sram_illegal_clear); break; default: KASSERT(B_FALSE, ("Impossible SRAM event")); break; } return (B_FALSE); } static boolean_t sfxge_ev_timer(void *arg, uint32_t index) { (void)arg; (void)index; return (B_FALSE); } static boolean_t sfxge_ev_wake_up(void *arg, uint32_t index) { (void)arg; (void)index; return (B_FALSE); } #if EFSYS_OPT_QSTATS static void +sfxge_evq_stat_update(struct sfxge_evq *evq) +{ + clock_t now; + + SFXGE_EVQ_LOCK(evq); + + if (__predict_false(evq->init_state != SFXGE_EVQ_STARTED)) + goto out; + + now = ticks; + if ((unsigned int)(now - evq->stats_update_time) < (unsigned int)hz) + goto out; + + evq->stats_update_time = now; + efx_ev_qstats_update(evq->common, evq->stats); + +out: + SFXGE_EVQ_UNLOCK(evq); +} + +static int +sfxge_evq_stat_handler(SYSCTL_HANDLER_ARGS) +{ + struct sfxge_evq *evq = arg1; + struct sfxge_softc *sc = evq->sc; + unsigned int id = arg2; + + SFXGE_ADAPTER_LOCK(sc); + + sfxge_evq_stat_update(evq); + + SFXGE_ADAPTER_UNLOCK(sc); + + return (SYSCTL_OUT(req, &evq->stats[id], sizeof(evq->stats[id]))); +} + +static int +sfxge_evq_stat_init(struct sfxge_evq *evq) +{ + struct sfxge_softc *sc = evq->sc; + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); + char name[16]; + struct sysctl_oid *evq_stats_node; + unsigned int id; + + snprintf(name, sizeof(name), "%u", evq->index); + evq_stats_node = SYSCTL_ADD_NODE(ctx, + SYSCTL_CHILDREN(sc->evqs_stats_node), + OID_AUTO, name, CTLFLAG_RD, NULL, ""); + if (evq_stats_node == NULL) + return (ENOMEM); + + for (id = 0; id < EV_NQSTATS; id++) { + SYSCTL_ADD_PROC( + ctx, SYSCTL_CHILDREN(evq_stats_node), + OID_AUTO, efx_ev_qstat_name(sc->enp, id), + CTLTYPE_U64|CTLFLAG_RD, + evq, id, sfxge_evq_stat_handler, "Q", + ""); + } + + return (0); +} + +static void sfxge_ev_stat_update(struct sfxge_softc *sc) { struct sfxge_evq *evq; unsigned int index; clock_t now; + unsigned int id; SFXGE_ADAPTER_LOCK(sc); - if (__predict_false(sc->evq[0]->init_state != SFXGE_EVQ_STARTED)) - goto out; - now = ticks; if ((unsigned int)(now - sc->ev_stats_update_time) < (unsigned int)hz) goto out; sc->ev_stats_update_time = now; - /* Add event counts from each event queue in turn */ + memset(sc->ev_stats, 0, sizeof(sc->ev_stats)); + + /* Update and add event counts from each event queue in turn */ for (index = 0; index < sc->evq_count; index++) { evq = sc->evq[index]; - SFXGE_EVQ_LOCK(evq); - efx_ev_qstats_update(evq->common, sc->ev_stats); - SFXGE_EVQ_UNLOCK(evq); + sfxge_evq_stat_update(evq); + for (id = 0; id < EV_NQSTATS; id++) + sc->ev_stats[id] += evq->stats[id]; } out: SFXGE_ADAPTER_UNLOCK(sc); } static int sfxge_ev_stat_handler(SYSCTL_HANDLER_ARGS) { struct sfxge_softc *sc = arg1; unsigned int id = arg2; sfxge_ev_stat_update(sc); return (SYSCTL_OUT(req, &sc->ev_stats[id], sizeof(sc->ev_stats[id]))); } static void sfxge_ev_stat_init(struct sfxge_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); struct sysctl_oid_list *stat_list; unsigned int id; char name[40]; stat_list = SYSCTL_CHILDREN(sc->stats_node); for (id = 0; id < EV_NQSTATS; id++) { snprintf(name, sizeof(name), "ev_%s", efx_ev_qstat_name(sc->enp, id)); SYSCTL_ADD_PROC( ctx, stat_list, OID_AUTO, name, CTLTYPE_U64|CTLFLAG_RD, sc, id, sfxge_ev_stat_handler, "Q", ""); } } #endif /* EFSYS_OPT_QSTATS */ static void sfxge_ev_qmoderate(struct sfxge_softc *sc, unsigned int idx, unsigned int us) { struct sfxge_evq *evq; efx_evq_t *eep; evq = sc->evq[idx]; eep = evq->common; KASSERT(evq->init_state == SFXGE_EVQ_STARTED, ("evq->init_state != SFXGE_EVQ_STARTED")); (void)efx_ev_qmoderate(eep, us); } static int sfxge_int_mod_handler(SYSCTL_HANDLER_ARGS) { struct sfxge_softc *sc = arg1; struct sfxge_intr *intr = &sc->intr; unsigned int moderation; int error; unsigned int index; SFXGE_ADAPTER_LOCK(sc); if (req->newptr != NULL) { if ((error = SYSCTL_IN(req, &moderation, sizeof(moderation))) != 0) goto out; /* We may not be calling efx_ev_qmoderate() now, * so we have to range-check the value ourselves. */ if (moderation > efx_nic_cfg_get(sc->enp)->enc_evq_timer_max_us) { error = EINVAL; goto out; } sc->ev_moderation = moderation; if (intr->state == SFXGE_INTR_STARTED) { for (index = 0; index < sc->evq_count; index++) sfxge_ev_qmoderate(sc, index, moderation); } } else { error = SYSCTL_OUT(req, &sc->ev_moderation, sizeof(sc->ev_moderation)); } out: SFXGE_ADAPTER_UNLOCK(sc); return (error); } static boolean_t sfxge_ev_initialized(void *arg) { struct sfxge_evq *evq; evq = (struct sfxge_evq *)arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); /* Init done events may be duplicated on 7xxx */ KASSERT(evq->init_state == SFXGE_EVQ_STARTING || evq->init_state == SFXGE_EVQ_STARTED, ("evq not starting")); evq->init_state = SFXGE_EVQ_STARTED; return (0); } static boolean_t sfxge_ev_link_change(void *arg, efx_link_mode_t link_mode) { struct sfxge_evq *evq; struct sfxge_softc *sc; evq = (struct sfxge_evq *)arg; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); sc = evq->sc; sfxge_mac_link_update(sc, link_mode); return (0); } static const efx_ev_callbacks_t sfxge_ev_callbacks = { .eec_initialized = sfxge_ev_initialized, .eec_rx = sfxge_ev_rx, .eec_tx = sfxge_ev_tx, .eec_exception = sfxge_ev_exception, .eec_rxq_flush_done = sfxge_ev_rxq_flush_done, .eec_rxq_flush_failed = sfxge_ev_rxq_flush_failed, .eec_txq_flush_done = sfxge_ev_txq_flush_done, .eec_software = sfxge_ev_software, .eec_sram = sfxge_ev_sram, .eec_wake_up = sfxge_ev_wake_up, .eec_timer = sfxge_ev_timer, .eec_link_change = sfxge_ev_link_change, }; int sfxge_ev_qpoll(struct sfxge_evq *evq) { int rc; SFXGE_EVQ_LOCK(evq); if (__predict_false(evq->init_state != SFXGE_EVQ_STARTING && evq->init_state != SFXGE_EVQ_STARTED)) { rc = EINVAL; goto fail; } /* Synchronize the DMA memory for reading */ bus_dmamap_sync(evq->mem.esm_tag, evq->mem.esm_map, BUS_DMASYNC_POSTREAD); KASSERT(evq->rx_done == 0, ("evq->rx_done != 0")); KASSERT(evq->tx_done == 0, ("evq->tx_done != 0")); KASSERT(evq->txq == NULL, ("evq->txq != NULL")); KASSERT(evq->txqs == &evq->txq, ("evq->txqs != &evq->txq")); /* Poll the queue */ efx_ev_qpoll(evq->common, &evq->read_ptr, &sfxge_ev_callbacks, evq); evq->rx_done = 0; evq->tx_done = 0; /* Perform any pending completion processing */ sfxge_ev_qcomplete(evq, B_TRUE); /* Re-prime the event queue for interrupts */ if ((rc = efx_ev_qprime(evq->common, evq->read_ptr)) != 0) goto fail; SFXGE_EVQ_UNLOCK(evq); return (0); fail: SFXGE_EVQ_UNLOCK(evq); return (rc); } static void sfxge_ev_qstop(struct sfxge_softc *sc, unsigned int index) { struct sfxge_evq *evq; evq = sc->evq[index]; KASSERT(evq->init_state == SFXGE_EVQ_STARTED, ("evq->init_state != SFXGE_EVQ_STARTED")); SFXGE_EVQ_LOCK(evq); evq->init_state = SFXGE_EVQ_INITIALIZED; evq->read_ptr = 0; evq->exception = B_FALSE; #if EFSYS_OPT_QSTATS /* Add event counts before discarding the common evq state */ - efx_ev_qstats_update(evq->common, sc->ev_stats); + efx_ev_qstats_update(evq->common, evq->stats); #endif efx_ev_qdestroy(evq->common); efx_sram_buf_tbl_clear(sc->enp, evq->buf_base_id, EFX_EVQ_NBUFS(evq->entries)); SFXGE_EVQ_UNLOCK(evq); } static int sfxge_ev_qstart(struct sfxge_softc *sc, unsigned int index) { struct sfxge_evq *evq; efsys_mem_t *esmp; int count; int rc; evq = sc->evq[index]; esmp = &evq->mem; KASSERT(evq->init_state == SFXGE_EVQ_INITIALIZED, ("evq->init_state != SFXGE_EVQ_INITIALIZED")); /* Clear all events. */ (void)memset(esmp->esm_base, 0xff, EFX_EVQ_SIZE(evq->entries)); /* Program the buffer table. */ if ((rc = efx_sram_buf_tbl_set(sc->enp, evq->buf_base_id, esmp, EFX_EVQ_NBUFS(evq->entries))) != 0) return (rc); /* Create the common code event queue. */ if ((rc = efx_ev_qcreate(sc->enp, index, esmp, evq->entries, evq->buf_base_id, sc->ev_moderation, EFX_EVQ_FLAGS_TYPE_AUTO, &evq->common)) != 0) goto fail; SFXGE_EVQ_LOCK(evq); /* Prime the event queue for interrupts */ if ((rc = efx_ev_qprime(evq->common, evq->read_ptr)) != 0) goto fail2; evq->init_state = SFXGE_EVQ_STARTING; SFXGE_EVQ_UNLOCK(evq); /* Wait for the initialization event */ count = 0; do { /* Pause for 100 ms */ pause("sfxge evq init", hz / 10); /* Check to see if the test event has been processed */ if (evq->init_state == SFXGE_EVQ_STARTED) goto done; } while (++count < 20); rc = ETIMEDOUT; goto fail3; done: return (0); fail3: SFXGE_EVQ_LOCK(evq); evq->init_state = SFXGE_EVQ_INITIALIZED; fail2: SFXGE_EVQ_UNLOCK(evq); efx_ev_qdestroy(evq->common); fail: efx_sram_buf_tbl_clear(sc->enp, evq->buf_base_id, EFX_EVQ_NBUFS(evq->entries)); return (rc); } void sfxge_ev_stop(struct sfxge_softc *sc) { struct sfxge_intr *intr; efx_nic_t *enp; int index; intr = &sc->intr; enp = sc->enp; KASSERT(intr->state == SFXGE_INTR_STARTED, ("Interrupts not started")); /* Stop the event queue(s) */ index = sc->evq_count; while (--index >= 0) sfxge_ev_qstop(sc, index); /* Tear down the event module */ efx_ev_fini(enp); } int sfxge_ev_start(struct sfxge_softc *sc) { struct sfxge_intr *intr; int index; int rc; intr = &sc->intr; KASSERT(intr->state == SFXGE_INTR_STARTED, ("intr->state != SFXGE_INTR_STARTED")); /* Initialize the event module */ if ((rc = efx_ev_init(sc->enp)) != 0) return (rc); /* Start the event queues */ for (index = 0; index < sc->evq_count; index++) { if ((rc = sfxge_ev_qstart(sc, index)) != 0) goto fail; } return (0); fail: /* Stop the event queue(s) */ while (--index >= 0) sfxge_ev_qstop(sc, index); /* Tear down the event module */ efx_ev_fini(sc->enp); return (rc); } static void sfxge_ev_qfini(struct sfxge_softc *sc, unsigned int index) { struct sfxge_evq *evq; evq = sc->evq[index]; KASSERT(evq->init_state == SFXGE_EVQ_INITIALIZED, ("evq->init_state != SFXGE_EVQ_INITIALIZED")); KASSERT(evq->txqs == &evq->txq, ("evq->txqs != &evq->txq")); sfxge_dma_free(&evq->mem); sc->evq[index] = NULL; SFXGE_EVQ_LOCK_DESTROY(evq); free(evq, M_SFXGE); } static int sfxge_ev_qinit(struct sfxge_softc *sc, unsigned int index) { struct sfxge_evq *evq; efsys_mem_t *esmp; int rc; KASSERT(index < SFXGE_RX_SCALE_MAX, ("index >= SFXGE_RX_SCALE_MAX")); evq = malloc(sizeof(struct sfxge_evq), M_SFXGE, M_ZERO | M_WAITOK); evq->sc = sc; evq->index = index; sc->evq[index] = evq; esmp = &evq->mem; /* Build an event queue with room for one event per tx and rx buffer, * plus some extra for link state events and MCDI completions. * There are three tx queues in the first event queue and one in * other. */ if (index == 0) evq->entries = ROUNDUP_POW_OF_TWO(sc->rxq_entries + 3 * sc->txq_entries + 128); else evq->entries = ROUNDUP_POW_OF_TWO(sc->rxq_entries + sc->txq_entries + 128); /* Initialise TX completion list */ evq->txqs = &evq->txq; /* Allocate DMA space. */ if ((rc = sfxge_dma_alloc(sc, EFX_EVQ_SIZE(evq->entries), esmp)) != 0) return (rc); /* Allocate buffer table entries. */ sfxge_sram_buf_tbl_alloc(sc, EFX_EVQ_NBUFS(evq->entries), &evq->buf_base_id); SFXGE_EVQ_LOCK_INIT(evq, device_get_nameunit(sc->dev), index); evq->init_state = SFXGE_EVQ_INITIALIZED; +#if EFSYS_OPT_QSTATS + rc = sfxge_evq_stat_init(evq); + if (rc != 0) + goto fail_evq_stat_init; +#endif + return (0); + +#if EFSYS_OPT_QSTATS +fail_evq_stat_init: + evq->init_state = SFXGE_EVQ_UNINITIALIZED; + SFXGE_EVQ_LOCK_DESTROY(evq); + sfxge_dma_free(esmp); + sc->evq[index] = NULL; + free(evq, M_SFXGE); + + return (rc); +#endif } void sfxge_ev_fini(struct sfxge_softc *sc) { struct sfxge_intr *intr; int index; intr = &sc->intr; KASSERT(intr->state == SFXGE_INTR_INITIALIZED, ("intr->state != SFXGE_INTR_INITIALIZED")); sc->ev_moderation = 0; /* Tear down the event queue(s). */ index = sc->evq_count; while (--index >= 0) sfxge_ev_qfini(sc, index); sc->evq_count = 0; } int sfxge_ev_init(struct sfxge_softc *sc) { struct sysctl_ctx_list *sysctl_ctx = device_get_sysctl_ctx(sc->dev); struct sysctl_oid *sysctl_tree = device_get_sysctl_tree(sc->dev); struct sfxge_intr *intr; int index; int rc; intr = &sc->intr; sc->evq_count = intr->n_alloc; KASSERT(intr->state == SFXGE_INTR_INITIALIZED, ("intr->state != SFXGE_INTR_INITIALIZED")); /* Set default interrupt moderation; add a sysctl to * read and change it. */ sc->ev_moderation = SFXGE_MODERATION; SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "int_mod", CTLTYPE_UINT|CTLFLAG_RW, sc, 0, sfxge_int_mod_handler, "IU", "sfxge interrupt moderation (us)"); +#if EFSYS_OPT_QSTATS + sc->evqs_stats_node = SYSCTL_ADD_NODE( + device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(sc->stats_node), + OID_AUTO, "evq", CTLFLAG_RD, NULL, "Event queues stats"); + if (sc->evqs_stats_node == NULL) { + rc = ENOMEM; + goto fail_evqs_stats_node; + } +#endif + /* * Initialize the event queue(s) - one per interrupt. */ for (index = 0; index < sc->evq_count; index++) { if ((rc = sfxge_ev_qinit(sc, index)) != 0) goto fail; } #if EFSYS_OPT_QSTATS sfxge_ev_stat_init(sc); #endif return (0); fail: while (--index >= 0) sfxge_ev_qfini(sc, index); +#if EFSYS_OPT_QSTATS +fail_evqs_stats_node: +#endif sc->evq_count = 0; return (rc); } Index: projects/clang700-import/sys/dev/sfxge/sfxge_tx.c =================================================================== --- projects/clang700-import/sys/dev/sfxge/sfxge_tx.c (revision 341812) +++ projects/clang700-import/sys/dev/sfxge/sfxge_tx.c (revision 341813) @@ -1,2032 +1,2105 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010-2016 Solarflare Communications Inc. * All rights reserved. * * This software was developed in part by Philip Paeps under contract for * Solarflare Communications, 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. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the FreeBSD Project. */ /* Theory of operation: * - * Tx queues allocation and mapping + * Tx queues allocation and mapping on Siena * * One Tx queue with enabled checksum offload is allocated per Rx channel * (event queue). Also 2 Tx queues (one without checksum offload and one * with IP checksum offload only) are allocated and bound to event queue 0. * sfxge_txq_type is used as Tx queue label. * * So, event queue plus label mapping to Tx queue index is: * if event queue index is 0, TxQ-index = TxQ-label * [0..SFXGE_TXQ_NTYPES) * else TxQ-index = SFXGE_TXQ_NTYPES + EvQ-index - 1 * See sfxge_get_txq_by_label() sfxge_ev.c + * + * Tx queue allocation and mapping on EF10 + * + * One Tx queue with enabled checksum offload is allocated per Rx + * channel (event queue). Checksum offload on all Tx queues is enabled or + * disabled dynamically by inserting option descriptors, so the additional + * queues used on Siena are not required. + * + * TxQ label is always set to zero on EF10 hardware. + * So, event queue to Tx queue mapping is simple: + * TxQ-index = EvQ-index */ #include __FBSDID("$FreeBSD$"); #include "opt_rss.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RSS #include #endif #include "common/efx.h" #include "sfxge.h" #include "sfxge_tx.h" #define SFXGE_PARAM_TX_DPL_GET_MAX SFXGE_PARAM(tx_dpl_get_max) static int sfxge_tx_dpl_get_max = SFXGE_TX_DPL_GET_PKT_LIMIT_DEFAULT; TUNABLE_INT(SFXGE_PARAM_TX_DPL_GET_MAX, &sfxge_tx_dpl_get_max); SYSCTL_INT(_hw_sfxge, OID_AUTO, tx_dpl_get_max, CTLFLAG_RDTUN, &sfxge_tx_dpl_get_max, 0, "Maximum number of any packets in deferred packet get-list"); #define SFXGE_PARAM_TX_DPL_GET_NON_TCP_MAX \ SFXGE_PARAM(tx_dpl_get_non_tcp_max) static int sfxge_tx_dpl_get_non_tcp_max = SFXGE_TX_DPL_GET_NON_TCP_PKT_LIMIT_DEFAULT; TUNABLE_INT(SFXGE_PARAM_TX_DPL_GET_NON_TCP_MAX, &sfxge_tx_dpl_get_non_tcp_max); SYSCTL_INT(_hw_sfxge, OID_AUTO, tx_dpl_get_non_tcp_max, CTLFLAG_RDTUN, &sfxge_tx_dpl_get_non_tcp_max, 0, "Maximum number of non-TCP packets in deferred packet get-list"); #define SFXGE_PARAM_TX_DPL_PUT_MAX SFXGE_PARAM(tx_dpl_put_max) static int sfxge_tx_dpl_put_max = SFXGE_TX_DPL_PUT_PKT_LIMIT_DEFAULT; TUNABLE_INT(SFXGE_PARAM_TX_DPL_PUT_MAX, &sfxge_tx_dpl_put_max); SYSCTL_INT(_hw_sfxge, OID_AUTO, tx_dpl_put_max, CTLFLAG_RDTUN, &sfxge_tx_dpl_put_max, 0, "Maximum number of any packets in deferred packet put-list"); #define SFXGE_PARAM_TSO_FW_ASSISTED SFXGE_PARAM(tso_fw_assisted) static int sfxge_tso_fw_assisted = (SFXGE_FATSOV1 | SFXGE_FATSOV2); TUNABLE_INT(SFXGE_PARAM_TSO_FW_ASSISTED, &sfxge_tso_fw_assisted); SYSCTL_INT(_hw_sfxge, OID_AUTO, tso_fw_assisted, CTLFLAG_RDTUN, &sfxge_tso_fw_assisted, 0, "Bitmask of FW-assisted TSO allowed to use if supported by NIC firmware"); static const struct { const char *name; size_t offset; } sfxge_tx_stats[] = { #define SFXGE_TX_STAT(name, member) \ { #name, offsetof(struct sfxge_txq, member) } SFXGE_TX_STAT(tso_bursts, tso_bursts), SFXGE_TX_STAT(tso_packets, tso_packets), SFXGE_TX_STAT(tso_long_headers, tso_long_headers), SFXGE_TX_STAT(tso_pdrop_too_many, tso_pdrop_too_many), SFXGE_TX_STAT(tso_pdrop_no_rsrc, tso_pdrop_no_rsrc), SFXGE_TX_STAT(tx_collapses, collapses), SFXGE_TX_STAT(tx_drops, drops), SFXGE_TX_STAT(tx_get_overflow, get_overflow), SFXGE_TX_STAT(tx_get_non_tcp_overflow, get_non_tcp_overflow), SFXGE_TX_STAT(tx_put_overflow, put_overflow), SFXGE_TX_STAT(tx_netdown_drops, netdown_drops), }; /* Forward declarations. */ static void sfxge_tx_qdpl_service(struct sfxge_txq *txq); static void sfxge_tx_qlist_post(struct sfxge_txq *txq); static void sfxge_tx_qunblock(struct sfxge_txq *txq); static int sfxge_tx_queue_tso(struct sfxge_txq *txq, struct mbuf *mbuf, const bus_dma_segment_t *dma_seg, int n_dma_seg, - int vlan_tagged); + int n_extra_descs); +static inline void +sfxge_next_stmp(struct sfxge_txq *txq, struct sfxge_tx_mapping **pstmp) +{ + KASSERT((*pstmp)->flags == 0, ("stmp flags are not 0")); + if (__predict_false(*pstmp == + &txq->stmp[txq->ptr_mask])) + *pstmp = &txq->stmp[0]; + else + (*pstmp)++; +} + static int -sfxge_tx_maybe_insert_tag(struct sfxge_txq *txq, struct mbuf *mbuf) +sfxge_tx_maybe_toggle_cksum_offload(struct sfxge_txq *txq, struct mbuf *mbuf, + struct sfxge_tx_mapping **pstmp) { + uint16_t new_hw_cksum_flags; + efx_desc_t *desc; + + if (mbuf->m_pkthdr.csum_flags & + (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6 | CSUM_TSO)) { + /* + * We always set EFX_TXQ_CKSUM_IPV4 here because this + * configuration is the most useful, and this won't + * cause any trouble in case of IPv6 traffic anyway. + */ + new_hw_cksum_flags = EFX_TXQ_CKSUM_IPV4 | EFX_TXQ_CKSUM_TCPUDP; + } else if (mbuf->m_pkthdr.csum_flags & CSUM_DELAY_IP) { + new_hw_cksum_flags = EFX_TXQ_CKSUM_IPV4; + } else { + new_hw_cksum_flags = 0; + } + + if (new_hw_cksum_flags == txq->hw_cksum_flags) + return (0); + + desc = &txq->pend_desc[txq->n_pend_desc]; + efx_tx_qdesc_checksum_create(txq->common, new_hw_cksum_flags, desc); + txq->hw_cksum_flags = new_hw_cksum_flags; + txq->n_pend_desc++; + + sfxge_next_stmp(txq, pstmp); + + return (1); +} + +static int +sfxge_tx_maybe_insert_tag(struct sfxge_txq *txq, struct mbuf *mbuf, + struct sfxge_tx_mapping **pstmp) +{ uint16_t this_tag = ((mbuf->m_flags & M_VLANTAG) ? mbuf->m_pkthdr.ether_vtag : 0); + efx_desc_t *desc; if (this_tag == txq->hw_vlan_tci) return (0); - efx_tx_qdesc_vlantci_create(txq->common, - bswap16(this_tag), - &txq->pend_desc[0]); - txq->n_pend_desc = 1; + desc = &txq->pend_desc[txq->n_pend_desc]; + efx_tx_qdesc_vlantci_create(txq->common, bswap16(this_tag), desc); txq->hw_vlan_tci = this_tag; + txq->n_pend_desc++; + + sfxge_next_stmp(txq, pstmp); + return (1); } -static inline void -sfxge_next_stmp(struct sfxge_txq *txq, struct sfxge_tx_mapping **pstmp) -{ - KASSERT((*pstmp)->flags == 0, ("stmp flags are not 0")); - if (__predict_false(*pstmp == - &txq->stmp[txq->ptr_mask])) - *pstmp = &txq->stmp[0]; - else - (*pstmp)++; -} - - void sfxge_tx_qcomplete(struct sfxge_txq *txq, struct sfxge_evq *evq) { unsigned int completed; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); completed = txq->completed; while (completed != txq->pending) { struct sfxge_tx_mapping *stmp; unsigned int id; id = completed++ & txq->ptr_mask; stmp = &txq->stmp[id]; if (stmp->flags & TX_BUF_UNMAP) { bus_dmamap_unload(txq->packet_dma_tag, stmp->map); if (stmp->flags & TX_BUF_MBUF) { struct mbuf *m = stmp->u.mbuf; do m = m_free(m); while (m != NULL); } else { free(stmp->u.heap_buf, M_SFXGE); } stmp->flags = 0; } } txq->completed = completed; /* Check whether we need to unblock the queue. */ mb(); if (txq->blocked) { unsigned int level; level = txq->added - txq->completed; if (level <= SFXGE_TXQ_UNBLOCK_LEVEL(txq->entries)) sfxge_tx_qunblock(txq); } } static unsigned int sfxge_is_mbuf_non_tcp(struct mbuf *mbuf) { /* Absence of TCP checksum flags does not mean that it is non-TCP * but it should be true if user wants to achieve high throughput. */ return (!(mbuf->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP6_TCP))); } /* * Reorder the put list and append it to the get list. */ static void sfxge_tx_qdpl_swizzle(struct sfxge_txq *txq) { struct sfxge_tx_dpl *stdp; struct mbuf *mbuf, *get_next, **get_tailp; volatile uintptr_t *putp; uintptr_t put; unsigned int count; unsigned int non_tcp_count; SFXGE_TXQ_LOCK_ASSERT_OWNED(txq); stdp = &txq->dpl; /* Acquire the put list. */ putp = &stdp->std_put; put = atomic_readandclear_ptr(putp); mbuf = (void *)put; if (mbuf == NULL) return; /* Reverse the put list. */ get_tailp = &mbuf->m_nextpkt; get_next = NULL; count = 0; non_tcp_count = 0; do { struct mbuf *put_next; non_tcp_count += sfxge_is_mbuf_non_tcp(mbuf); put_next = mbuf->m_nextpkt; mbuf->m_nextpkt = get_next; get_next = mbuf; mbuf = put_next; count++; } while (mbuf != NULL); if (count > stdp->std_put_hiwat) stdp->std_put_hiwat = count; /* Append the reversed put list to the get list. */ KASSERT(*get_tailp == NULL, ("*get_tailp != NULL")); *stdp->std_getp = get_next; stdp->std_getp = get_tailp; stdp->std_get_count += count; stdp->std_get_non_tcp_count += non_tcp_count; } static void sfxge_tx_qreap(struct sfxge_txq *txq) { SFXGE_TXQ_LOCK_ASSERT_OWNED(txq); txq->reaped = txq->completed; } static void sfxge_tx_qlist_post(struct sfxge_txq *txq) { unsigned int old_added; unsigned int block_level; unsigned int level; int rc; SFXGE_TXQ_LOCK_ASSERT_OWNED(txq); KASSERT(txq->n_pend_desc != 0, ("txq->n_pend_desc == 0")); KASSERT(txq->n_pend_desc <= txq->max_pkt_desc, ("txq->n_pend_desc too large")); KASSERT(!txq->blocked, ("txq->blocked")); old_added = txq->added; /* Post the fragment list. */ rc = efx_tx_qdesc_post(txq->common, txq->pend_desc, txq->n_pend_desc, txq->reaped, &txq->added); KASSERT(rc == 0, ("efx_tx_qdesc_post() failed")); /* If efx_tx_qdesc_post() had to refragment, our information about * buffers to free may be associated with the wrong * descriptors. */ KASSERT(txq->added - old_added == txq->n_pend_desc, ("efx_tx_qdesc_post() refragmented descriptors")); level = txq->added - txq->reaped; KASSERT(level <= txq->entries, ("overfilled TX queue")); /* Clear the fragment list. */ txq->n_pend_desc = 0; /* * Set the block level to ensure there is space to generate a * large number of descriptors for TSO. */ block_level = EFX_TXQ_LIMIT(txq->entries) - txq->max_pkt_desc; /* Have we reached the block level? */ if (level < block_level) return; /* Reap, and check again */ sfxge_tx_qreap(txq); level = txq->added - txq->reaped; if (level < block_level) return; txq->blocked = 1; /* * Avoid a race with completion interrupt handling that could leave * the queue blocked. */ mb(); sfxge_tx_qreap(txq); level = txq->added - txq->reaped; if (level < block_level) { mb(); txq->blocked = 0; } } static int sfxge_tx_queue_mbuf(struct sfxge_txq *txq, struct mbuf *mbuf) { bus_dmamap_t *used_map; bus_dmamap_t map; bus_dma_segment_t dma_seg[SFXGE_TX_MAPPING_MAX_SEG]; unsigned int id; struct sfxge_tx_mapping *stmp; efx_desc_t *desc; int n_dma_seg; int rc; int i; int eop; + uint16_t hw_cksum_flags_prev; uint16_t hw_vlan_tci_prev; - int vlan_tagged; + int n_extra_descs; KASSERT(!txq->blocked, ("txq->blocked")); #if SFXGE_TX_PARSE_EARLY /* * If software TSO is used, we still need to copy packet header, * even if we have already parsed it early before enqueue. */ if ((mbuf->m_pkthdr.csum_flags & CSUM_TSO) && (txq->tso_fw_assisted == 0)) prefetch_read_many(mbuf->m_data); #else /* * Prefetch packet header since we need to parse it and extract * IP ID, TCP sequence number and flags. */ if (mbuf->m_pkthdr.csum_flags & CSUM_TSO) prefetch_read_many(mbuf->m_data); #endif if (__predict_false(txq->init_state != SFXGE_TXQ_STARTED)) { rc = EINTR; goto reject; } /* Load the packet for DMA. */ id = txq->added & txq->ptr_mask; stmp = &txq->stmp[id]; rc = bus_dmamap_load_mbuf_sg(txq->packet_dma_tag, stmp->map, mbuf, dma_seg, &n_dma_seg, 0); if (rc == EFBIG) { /* Try again. */ struct mbuf *new_mbuf = m_collapse(mbuf, M_NOWAIT, SFXGE_TX_MAPPING_MAX_SEG); if (new_mbuf == NULL) goto reject; ++txq->collapses; mbuf = new_mbuf; rc = bus_dmamap_load_mbuf_sg(txq->packet_dma_tag, stmp->map, mbuf, dma_seg, &n_dma_seg, 0); } if (rc != 0) goto reject; /* Make the packet visible to the hardware. */ bus_dmamap_sync(txq->packet_dma_tag, stmp->map, BUS_DMASYNC_PREWRITE); used_map = &stmp->map; + hw_cksum_flags_prev = txq->hw_cksum_flags; hw_vlan_tci_prev = txq->hw_vlan_tci; - vlan_tagged = sfxge_tx_maybe_insert_tag(txq, mbuf); - if (vlan_tagged) { - sfxge_next_stmp(txq, &stmp); - } + /* + * The order of option descriptors, which are used to leverage VLAN tag + * and checksum offloads, might be important. Changing checksum offload + * between VLAN option and packet descriptors probably does not work. + */ + n_extra_descs = sfxge_tx_maybe_toggle_cksum_offload(txq, mbuf, &stmp); + n_extra_descs += sfxge_tx_maybe_insert_tag(txq, mbuf, &stmp); + if (mbuf->m_pkthdr.csum_flags & CSUM_TSO) { - rc = sfxge_tx_queue_tso(txq, mbuf, dma_seg, n_dma_seg, vlan_tagged); + rc = sfxge_tx_queue_tso(txq, mbuf, dma_seg, n_dma_seg, + n_extra_descs); if (rc < 0) goto reject_mapped; stmp = &txq->stmp[(rc - 1) & txq->ptr_mask]; } else { /* Add the mapping to the fragment list, and set flags * for the buffer. */ i = 0; for (;;) { - desc = &txq->pend_desc[i + vlan_tagged]; + desc = &txq->pend_desc[i + n_extra_descs]; eop = (i == n_dma_seg - 1); efx_tx_qdesc_dma_create(txq->common, dma_seg[i].ds_addr, dma_seg[i].ds_len, eop, desc); if (eop) break; i++; sfxge_next_stmp(txq, &stmp); } - txq->n_pend_desc = n_dma_seg + vlan_tagged; + txq->n_pend_desc = n_dma_seg + n_extra_descs; } /* * If the mapping required more than one descriptor * then we need to associate the DMA map with the last * descriptor, not the first. */ if (used_map != &stmp->map) { map = stmp->map; stmp->map = *used_map; *used_map = map; } stmp->u.mbuf = mbuf; stmp->flags = TX_BUF_UNMAP | TX_BUF_MBUF; /* Post the fragment list. */ sfxge_tx_qlist_post(txq); return (0); reject_mapped: txq->hw_vlan_tci = hw_vlan_tci_prev; + txq->hw_cksum_flags = hw_cksum_flags_prev; bus_dmamap_unload(txq->packet_dma_tag, *used_map); reject: /* Drop the packet on the floor. */ m_freem(mbuf); ++txq->drops; return (rc); } /* * Drain the deferred packet list into the transmit queue. */ static void sfxge_tx_qdpl_drain(struct sfxge_txq *txq) { struct sfxge_softc *sc; struct sfxge_tx_dpl *stdp; struct mbuf *mbuf, *next; unsigned int count; unsigned int non_tcp_count; unsigned int pushed; int rc; SFXGE_TXQ_LOCK_ASSERT_OWNED(txq); sc = txq->sc; stdp = &txq->dpl; pushed = txq->added; if (__predict_true(txq->init_state == SFXGE_TXQ_STARTED)) { prefetch_read_many(sc->enp); prefetch_read_many(txq->common); } mbuf = stdp->std_get; count = stdp->std_get_count; non_tcp_count = stdp->std_get_non_tcp_count; if (count > stdp->std_get_hiwat) stdp->std_get_hiwat = count; while (count != 0) { KASSERT(mbuf != NULL, ("mbuf == NULL")); next = mbuf->m_nextpkt; mbuf->m_nextpkt = NULL; ETHER_BPF_MTAP(sc->ifnet, mbuf); /* packet capture */ if (next != NULL) prefetch_read_many(next); rc = sfxge_tx_queue_mbuf(txq, mbuf); --count; non_tcp_count -= sfxge_is_mbuf_non_tcp(mbuf); mbuf = next; if (rc != 0) continue; if (txq->blocked) break; /* Push the fragments to the hardware in batches. */ if (txq->added - pushed >= SFXGE_TX_BATCH) { efx_tx_qpush(txq->common, txq->added, pushed); pushed = txq->added; } } if (count == 0) { KASSERT(mbuf == NULL, ("mbuf != NULL")); KASSERT(non_tcp_count == 0, ("inconsistent TCP/non-TCP detection")); stdp->std_get = NULL; stdp->std_get_count = 0; stdp->std_get_non_tcp_count = 0; stdp->std_getp = &stdp->std_get; } else { stdp->std_get = mbuf; stdp->std_get_count = count; stdp->std_get_non_tcp_count = non_tcp_count; } if (txq->added != pushed) efx_tx_qpush(txq->common, txq->added, pushed); KASSERT(txq->blocked || stdp->std_get_count == 0, ("queue unblocked but count is non-zero")); } #define SFXGE_TX_QDPL_PENDING(_txq) ((_txq)->dpl.std_put != 0) /* * Service the deferred packet list. * * NOTE: drops the txq mutex! */ static void sfxge_tx_qdpl_service(struct sfxge_txq *txq) { SFXGE_TXQ_LOCK_ASSERT_OWNED(txq); do { if (SFXGE_TX_QDPL_PENDING(txq)) sfxge_tx_qdpl_swizzle(txq); if (!txq->blocked) sfxge_tx_qdpl_drain(txq); SFXGE_TXQ_UNLOCK(txq); } while (SFXGE_TX_QDPL_PENDING(txq) && SFXGE_TXQ_TRYLOCK(txq)); } /* * Put a packet on the deferred packet get-list. */ static int sfxge_tx_qdpl_put_locked(struct sfxge_txq *txq, struct mbuf *mbuf) { struct sfxge_tx_dpl *stdp; stdp = &txq->dpl; KASSERT(mbuf->m_nextpkt == NULL, ("mbuf->m_nextpkt != NULL")); SFXGE_TXQ_LOCK_ASSERT_OWNED(txq); if (stdp->std_get_count >= stdp->std_get_max) { txq->get_overflow++; return (ENOBUFS); } if (sfxge_is_mbuf_non_tcp(mbuf)) { if (stdp->std_get_non_tcp_count >= stdp->std_get_non_tcp_max) { txq->get_non_tcp_overflow++; return (ENOBUFS); } stdp->std_get_non_tcp_count++; } *(stdp->std_getp) = mbuf; stdp->std_getp = &mbuf->m_nextpkt; stdp->std_get_count++; return (0); } /* * Put a packet on the deferred packet put-list. * * We overload the csum_data field in the mbuf to keep track of this length * because there is no cheap alternative to avoid races. */ static int sfxge_tx_qdpl_put_unlocked(struct sfxge_txq *txq, struct mbuf *mbuf) { struct sfxge_tx_dpl *stdp; volatile uintptr_t *putp; uintptr_t old; uintptr_t new; unsigned int put_count; KASSERT(mbuf->m_nextpkt == NULL, ("mbuf->m_nextpkt != NULL")); SFXGE_TXQ_LOCK_ASSERT_NOTOWNED(txq); stdp = &txq->dpl; putp = &stdp->std_put; new = (uintptr_t)mbuf; do { old = *putp; if (old != 0) { struct mbuf *mp = (struct mbuf *)old; put_count = mp->m_pkthdr.csum_data; } else put_count = 0; if (put_count >= stdp->std_put_max) { atomic_add_long(&txq->put_overflow, 1); return (ENOBUFS); } mbuf->m_pkthdr.csum_data = put_count + 1; mbuf->m_nextpkt = (void *)old; } while (atomic_cmpset_ptr(putp, old, new) == 0); return (0); } /* * Called from if_transmit - will try to grab the txq lock and enqueue to the * put list if it succeeds, otherwise try to push onto the defer list if space. */ static int sfxge_tx_packet_add(struct sfxge_txq *txq, struct mbuf *m) { int rc; if (!SFXGE_LINK_UP(txq->sc)) { atomic_add_long(&txq->netdown_drops, 1); return (ENETDOWN); } /* * Try to grab the txq lock. If we are able to get the lock, * the packet will be appended to the "get list" of the deferred * packet list. Otherwise, it will be pushed on the "put list". */ if (SFXGE_TXQ_TRYLOCK(txq)) { /* First swizzle put-list to get-list to keep order */ sfxge_tx_qdpl_swizzle(txq); rc = sfxge_tx_qdpl_put_locked(txq, m); /* Try to service the list. */ sfxge_tx_qdpl_service(txq); /* Lock has been dropped. */ } else { rc = sfxge_tx_qdpl_put_unlocked(txq, m); /* * Try to grab the lock again. * * If we are able to get the lock, we need to process * the deferred packet list. If we are not able to get * the lock, another thread is processing the list. */ if ((rc == 0) && SFXGE_TXQ_TRYLOCK(txq)) { sfxge_tx_qdpl_service(txq); /* Lock has been dropped. */ } } SFXGE_TXQ_LOCK_ASSERT_NOTOWNED(txq); return (rc); } static void sfxge_tx_qdpl_flush(struct sfxge_txq *txq) { struct sfxge_tx_dpl *stdp = &txq->dpl; struct mbuf *mbuf, *next; SFXGE_TXQ_LOCK(txq); sfxge_tx_qdpl_swizzle(txq); for (mbuf = stdp->std_get; mbuf != NULL; mbuf = next) { next = mbuf->m_nextpkt; m_freem(mbuf); } stdp->std_get = NULL; stdp->std_get_count = 0; stdp->std_get_non_tcp_count = 0; stdp->std_getp = &stdp->std_get; SFXGE_TXQ_UNLOCK(txq); } void sfxge_if_qflush(struct ifnet *ifp) { struct sfxge_softc *sc; unsigned int i; sc = ifp->if_softc; for (i = 0; i < sc->txq_count; i++) sfxge_tx_qdpl_flush(sc->txq[i]); } #if SFXGE_TX_PARSE_EARLY /* There is little space for user data in mbuf pkthdr, so we * use l*hlen fields which are not used by the driver otherwise * to store header offsets. * The fields are 8-bit, but it's ok, no header may be longer than 255 bytes. */ #define TSO_MBUF_PROTO(_mbuf) ((_mbuf)->m_pkthdr.PH_loc.sixteen[0]) /* We abuse l5hlen here because PH_loc can hold only 64 bits of data */ #define TSO_MBUF_FLAGS(_mbuf) ((_mbuf)->m_pkthdr.l5hlen) #define TSO_MBUF_PACKETID(_mbuf) ((_mbuf)->m_pkthdr.PH_loc.sixteen[1]) #define TSO_MBUF_SEQNUM(_mbuf) ((_mbuf)->m_pkthdr.PH_loc.thirtytwo[1]) static void sfxge_parse_tx_packet(struct mbuf *mbuf) { struct ether_header *eh = mtod(mbuf, struct ether_header *); const struct tcphdr *th; struct tcphdr th_copy; /* Find network protocol and header */ TSO_MBUF_PROTO(mbuf) = eh->ether_type; if (TSO_MBUF_PROTO(mbuf) == htons(ETHERTYPE_VLAN)) { struct ether_vlan_header *veh = mtod(mbuf, struct ether_vlan_header *); TSO_MBUF_PROTO(mbuf) = veh->evl_proto; mbuf->m_pkthdr.l2hlen = sizeof(*veh); } else { mbuf->m_pkthdr.l2hlen = sizeof(*eh); } /* Find TCP header */ if (TSO_MBUF_PROTO(mbuf) == htons(ETHERTYPE_IP)) { const struct ip *iph = (const struct ip *)mtodo(mbuf, mbuf->m_pkthdr.l2hlen); KASSERT(iph->ip_p == IPPROTO_TCP, ("TSO required on non-TCP packet")); mbuf->m_pkthdr.l3hlen = mbuf->m_pkthdr.l2hlen + 4 * iph->ip_hl; TSO_MBUF_PACKETID(mbuf) = iph->ip_id; } else { KASSERT(TSO_MBUF_PROTO(mbuf) == htons(ETHERTYPE_IPV6), ("TSO required on non-IP packet")); KASSERT(((const struct ip6_hdr *)mtodo(mbuf, mbuf->m_pkthdr.l2hlen))->ip6_nxt == IPPROTO_TCP, ("TSO required on non-TCP packet")); mbuf->m_pkthdr.l3hlen = mbuf->m_pkthdr.l2hlen + sizeof(struct ip6_hdr); TSO_MBUF_PACKETID(mbuf) = 0; } KASSERT(mbuf->m_len >= mbuf->m_pkthdr.l3hlen, ("network header is fragmented in mbuf")); /* We need TCP header including flags (window is the next) */ if (mbuf->m_len < mbuf->m_pkthdr.l3hlen + offsetof(struct tcphdr, th_win)) { m_copydata(mbuf, mbuf->m_pkthdr.l3hlen, sizeof(th_copy), (caddr_t)&th_copy); th = &th_copy; } else { th = (const struct tcphdr *)mtodo(mbuf, mbuf->m_pkthdr.l3hlen); } mbuf->m_pkthdr.l4hlen = mbuf->m_pkthdr.l3hlen + 4 * th->th_off; TSO_MBUF_SEQNUM(mbuf) = ntohl(th->th_seq); /* These flags must not be duplicated */ /* * RST should not be duplicated as well, but FreeBSD kernel * generates TSO packets with RST flag. So, do not assert * its absence. */ KASSERT(!(th->th_flags & (TH_URG | TH_SYN)), ("incompatible TCP flag 0x%x on TSO packet", th->th_flags & (TH_URG | TH_SYN))); TSO_MBUF_FLAGS(mbuf) = th->th_flags; } #endif /* * TX start -- called by the stack. */ int sfxge_if_transmit(struct ifnet *ifp, struct mbuf *m) { struct sfxge_softc *sc; struct sfxge_txq *txq; int rc; sc = (struct sfxge_softc *)ifp->if_softc; /* * Transmit may be called when interface is up from the kernel * point of view, but not yet up (in progress) from the driver * point of view. I.e. link aggregation bring up. * Transmit may be called when interface is up from the driver * point of view, but already down from the kernel point of * view. I.e. Rx when interface shutdown is in progress. */ KASSERT((ifp->if_flags & IFF_UP) || (sc->if_flags & IFF_UP), ("interface not up")); /* Pick the desired transmit queue. */ - if (m->m_pkthdr.csum_flags & - (CSUM_DELAY_DATA | CSUM_TCP_IPV6 | CSUM_UDP_IPV6 | CSUM_TSO)) { + if (sc->txq_dynamic_cksum_toggle_supported | + (m->m_pkthdr.csum_flags & + (CSUM_DELAY_DATA | CSUM_TCP_IPV6 | CSUM_UDP_IPV6 | CSUM_TSO))) { int index = 0; #ifdef RSS uint32_t bucket_id; /* * Select a TX queue which matches the corresponding * RX queue for the hash in order to assign both * TX and RX parts of the flow to the same CPU */ if (rss_m2bucket(m, &bucket_id) == 0) index = bucket_id % (sc->txq_count - (SFXGE_TXQ_NTYPES - 1)); #else /* check if flowid is set */ if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) { uint32_t hash = m->m_pkthdr.flowid; uint32_t idx = hash % nitems(sc->rx_indir_table); index = sc->rx_indir_table[idx]; } #endif #if SFXGE_TX_PARSE_EARLY if (m->m_pkthdr.csum_flags & CSUM_TSO) sfxge_parse_tx_packet(m); #endif - txq = sc->txq[SFXGE_TXQ_IP_TCP_UDP_CKSUM + index]; + index += (sc->txq_dynamic_cksum_toggle_supported == B_FALSE) ? + SFXGE_TXQ_IP_TCP_UDP_CKSUM : 0; + txq = sc->txq[index]; } else if (m->m_pkthdr.csum_flags & CSUM_DELAY_IP) { txq = sc->txq[SFXGE_TXQ_IP_CKSUM]; } else { txq = sc->txq[SFXGE_TXQ_NON_CKSUM]; } rc = sfxge_tx_packet_add(txq, m); if (rc != 0) m_freem(m); return (rc); } /* * Software "TSO". Not quite as good as doing it in hardware, but * still faster than segmenting in the stack. */ struct sfxge_tso_state { /* Output position */ unsigned out_len; /* Remaining length in current segment */ unsigned seqnum; /* Current sequence number */ unsigned packet_space; /* Remaining space in current packet */ unsigned segs_space; /* Remaining number of DMA segments for the packet (FATSOv2 only) */ /* Input position */ uint64_t dma_addr; /* DMA address of current position */ unsigned in_len; /* Remaining length in current mbuf */ const struct mbuf *mbuf; /* Input mbuf (head of chain) */ u_short protocol; /* Network protocol (after VLAN decap) */ ssize_t nh_off; /* Offset of network header */ ssize_t tcph_off; /* Offset of TCP header */ unsigned header_len; /* Number of bytes of header */ unsigned seg_size; /* TCP segment size */ int fw_assisted; /* Use FW-assisted TSO */ u_short packet_id; /* IPv4 packet ID from the original packet */ uint8_t tcp_flags; /* TCP flags */ efx_desc_t header_desc; /* Precomputed header descriptor for * FW-assisted TSO */ }; #if !SFXGE_TX_PARSE_EARLY static const struct ip *tso_iph(const struct sfxge_tso_state *tso) { KASSERT(tso->protocol == htons(ETHERTYPE_IP), ("tso_iph() in non-IPv4 state")); return (const struct ip *)(tso->mbuf->m_data + tso->nh_off); } static __unused const struct ip6_hdr *tso_ip6h(const struct sfxge_tso_state *tso) { KASSERT(tso->protocol == htons(ETHERTYPE_IPV6), ("tso_ip6h() in non-IPv6 state")); return (const struct ip6_hdr *)(tso->mbuf->m_data + tso->nh_off); } static const struct tcphdr *tso_tcph(const struct sfxge_tso_state *tso) { return (const struct tcphdr *)(tso->mbuf->m_data + tso->tcph_off); } #endif /* Size of preallocated TSO header buffers. Larger blocks must be * allocated from the heap. */ #define TSOH_STD_SIZE 128 /* At most half the descriptors in the queue at any time will refer to * a TSO header buffer, since they must always be followed by a * payload descriptor referring to an mbuf. */ #define TSOH_COUNT(_txq_entries) ((_txq_entries) / 2u) #define TSOH_PER_PAGE (PAGE_SIZE / TSOH_STD_SIZE) #define TSOH_PAGE_COUNT(_txq_entries) \ howmany(TSOH_COUNT(_txq_entries), TSOH_PER_PAGE) static int tso_init(struct sfxge_txq *txq) { struct sfxge_softc *sc = txq->sc; unsigned int tsoh_page_count = TSOH_PAGE_COUNT(sc->txq_entries); int i, rc; /* Allocate TSO header buffers */ txq->tsoh_buffer = malloc(tsoh_page_count * sizeof(txq->tsoh_buffer[0]), M_SFXGE, M_WAITOK); for (i = 0; i < tsoh_page_count; i++) { rc = sfxge_dma_alloc(sc, PAGE_SIZE, &txq->tsoh_buffer[i]); if (rc != 0) goto fail; } return (0); fail: while (i-- > 0) sfxge_dma_free(&txq->tsoh_buffer[i]); free(txq->tsoh_buffer, M_SFXGE); txq->tsoh_buffer = NULL; return (rc); } static void tso_fini(struct sfxge_txq *txq) { int i; if (txq->tsoh_buffer != NULL) { for (i = 0; i < TSOH_PAGE_COUNT(txq->sc->txq_entries); i++) sfxge_dma_free(&txq->tsoh_buffer[i]); free(txq->tsoh_buffer, M_SFXGE); } } static void tso_start(struct sfxge_txq *txq, struct sfxge_tso_state *tso, const bus_dma_segment_t *hdr_dma_seg, struct mbuf *mbuf) { const efx_nic_cfg_t *encp = efx_nic_cfg_get(txq->sc->enp); #if !SFXGE_TX_PARSE_EARLY struct ether_header *eh = mtod(mbuf, struct ether_header *); const struct tcphdr *th; struct tcphdr th_copy; #endif tso->fw_assisted = txq->tso_fw_assisted; tso->mbuf = mbuf; /* Find network protocol and header */ #if !SFXGE_TX_PARSE_EARLY tso->protocol = eh->ether_type; if (tso->protocol == htons(ETHERTYPE_VLAN)) { struct ether_vlan_header *veh = mtod(mbuf, struct ether_vlan_header *); tso->protocol = veh->evl_proto; tso->nh_off = sizeof(*veh); } else { tso->nh_off = sizeof(*eh); } #else tso->protocol = TSO_MBUF_PROTO(mbuf); tso->nh_off = mbuf->m_pkthdr.l2hlen; tso->tcph_off = mbuf->m_pkthdr.l3hlen; tso->packet_id = ntohs(TSO_MBUF_PACKETID(mbuf)); #endif #if !SFXGE_TX_PARSE_EARLY /* Find TCP header */ if (tso->protocol == htons(ETHERTYPE_IP)) { KASSERT(tso_iph(tso)->ip_p == IPPROTO_TCP, ("TSO required on non-TCP packet")); tso->tcph_off = tso->nh_off + 4 * tso_iph(tso)->ip_hl; tso->packet_id = ntohs(tso_iph(tso)->ip_id); } else { KASSERT(tso->protocol == htons(ETHERTYPE_IPV6), ("TSO required on non-IP packet")); KASSERT(tso_ip6h(tso)->ip6_nxt == IPPROTO_TCP, ("TSO required on non-TCP packet")); tso->tcph_off = tso->nh_off + sizeof(struct ip6_hdr); tso->packet_id = 0; } #endif if (tso->fw_assisted && __predict_false(tso->tcph_off > encp->enc_tx_tso_tcp_header_offset_limit)) { tso->fw_assisted = 0; } #if !SFXGE_TX_PARSE_EARLY KASSERT(mbuf->m_len >= tso->tcph_off, ("network header is fragmented in mbuf")); /* We need TCP header including flags (window is the next) */ if (mbuf->m_len < tso->tcph_off + offsetof(struct tcphdr, th_win)) { m_copydata(tso->mbuf, tso->tcph_off, sizeof(th_copy), (caddr_t)&th_copy); th = &th_copy; } else { th = tso_tcph(tso); } tso->header_len = tso->tcph_off + 4 * th->th_off; #else tso->header_len = mbuf->m_pkthdr.l4hlen; #endif tso->seg_size = mbuf->m_pkthdr.tso_segsz; #if !SFXGE_TX_PARSE_EARLY tso->seqnum = ntohl(th->th_seq); /* These flags must not be duplicated */ /* * RST should not be duplicated as well, but FreeBSD kernel * generates TSO packets with RST flag. So, do not assert * its absence. */ KASSERT(!(th->th_flags & (TH_URG | TH_SYN)), ("incompatible TCP flag 0x%x on TSO packet", th->th_flags & (TH_URG | TH_SYN))); tso->tcp_flags = th->th_flags; #else tso->seqnum = TSO_MBUF_SEQNUM(mbuf); tso->tcp_flags = TSO_MBUF_FLAGS(mbuf); #endif tso->out_len = mbuf->m_pkthdr.len - tso->header_len; if (tso->fw_assisted) { if (hdr_dma_seg->ds_len >= tso->header_len) efx_tx_qdesc_dma_create(txq->common, hdr_dma_seg->ds_addr, tso->header_len, B_FALSE, &tso->header_desc); else tso->fw_assisted = 0; } } /* * tso_fill_packet_with_fragment - form descriptors for the current fragment * * Form descriptors for the current fragment, until we reach the end * of fragment or end-of-packet. Return 0 on success, 1 if not enough * space. */ static void tso_fill_packet_with_fragment(struct sfxge_txq *txq, struct sfxge_tso_state *tso) { efx_desc_t *desc; int n; uint64_t dma_addr = tso->dma_addr; boolean_t eop; if (tso->in_len == 0 || tso->packet_space == 0) return; KASSERT(tso->in_len > 0, ("TSO input length went negative")); KASSERT(tso->packet_space > 0, ("TSO packet space went negative")); if (tso->fw_assisted & SFXGE_FATSOV2) { n = tso->in_len; tso->out_len -= n; tso->seqnum += n; tso->in_len = 0; if (n < tso->packet_space) { tso->packet_space -= n; tso->segs_space--; } else { tso->packet_space = tso->seg_size - (n - tso->packet_space) % tso->seg_size; tso->segs_space = EFX_TX_FATSOV2_DMA_SEGS_PER_PKT_MAX - 1 - (tso->packet_space != tso->seg_size); } } else { n = min(tso->in_len, tso->packet_space); tso->packet_space -= n; tso->out_len -= n; tso->dma_addr += n; tso->in_len -= n; } /* * It is OK to use binary OR below to avoid extra branching * since all conditions may always be checked. */ eop = (tso->out_len == 0) | (tso->packet_space == 0) | (tso->segs_space == 0); desc = &txq->pend_desc[txq->n_pend_desc++]; efx_tx_qdesc_dma_create(txq->common, dma_addr, n, eop, desc); } /* Callback from bus_dmamap_load() for long TSO headers. */ static void tso_map_long_header(void *dma_addr_ret, bus_dma_segment_t *segs, int nseg, int error) { *(uint64_t *)dma_addr_ret = ((__predict_true(error == 0) && __predict_true(nseg == 1)) ? segs->ds_addr : 0); } /* * tso_start_new_packet - generate a new header and prepare for the new packet * * Generate a new header and prepare for the new packet. Return 0 on * success, or an error code if failed to alloc header. */ static int tso_start_new_packet(struct sfxge_txq *txq, struct sfxge_tso_state *tso, unsigned int *idp) { unsigned int id = *idp; struct tcphdr *tsoh_th; unsigned ip_length; caddr_t header; uint64_t dma_addr; bus_dmamap_t map; efx_desc_t *desc; int rc; if (tso->fw_assisted) { if (tso->fw_assisted & SFXGE_FATSOV2) { /* Add 2 FATSOv2 option descriptors */ desc = &txq->pend_desc[txq->n_pend_desc]; efx_tx_qdesc_tso2_create(txq->common, tso->packet_id, 0, tso->seqnum, tso->seg_size, desc, EFX_TX_FATSOV2_OPT_NDESCS); desc += EFX_TX_FATSOV2_OPT_NDESCS; txq->n_pend_desc += EFX_TX_FATSOV2_OPT_NDESCS; KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0")); id = (id + EFX_TX_FATSOV2_OPT_NDESCS) & txq->ptr_mask; tso->segs_space = EFX_TX_FATSOV2_DMA_SEGS_PER_PKT_MAX - 1; } else { uint8_t tcp_flags = tso->tcp_flags; if (tso->out_len > tso->seg_size) tcp_flags &= ~(TH_FIN | TH_PUSH); /* Add FATSOv1 option descriptor */ desc = &txq->pend_desc[txq->n_pend_desc++]; efx_tx_qdesc_tso_create(txq->common, tso->packet_id, tso->seqnum, tcp_flags, desc++); KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0")); id = (id + 1) & txq->ptr_mask; tso->seqnum += tso->seg_size; tso->segs_space = UINT_MAX; } /* Header DMA descriptor */ *desc = tso->header_desc; txq->n_pend_desc++; KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0")); id = (id + 1) & txq->ptr_mask; } else { /* Allocate a DMA-mapped header buffer. */ if (__predict_true(tso->header_len <= TSOH_STD_SIZE)) { unsigned int page_index = (id / 2) / TSOH_PER_PAGE; unsigned int buf_index = (id / 2) % TSOH_PER_PAGE; header = (txq->tsoh_buffer[page_index].esm_base + buf_index * TSOH_STD_SIZE); dma_addr = (txq->tsoh_buffer[page_index].esm_addr + buf_index * TSOH_STD_SIZE); map = txq->tsoh_buffer[page_index].esm_map; KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0")); } else { struct sfxge_tx_mapping *stmp = &txq->stmp[id]; /* We cannot use bus_dmamem_alloc() as that may sleep */ header = malloc(tso->header_len, M_SFXGE, M_NOWAIT); if (__predict_false(!header)) return (ENOMEM); rc = bus_dmamap_load(txq->packet_dma_tag, stmp->map, header, tso->header_len, tso_map_long_header, &dma_addr, BUS_DMA_NOWAIT); if (__predict_false(dma_addr == 0)) { if (rc == 0) { /* Succeeded but got >1 segment */ bus_dmamap_unload(txq->packet_dma_tag, stmp->map); rc = EINVAL; } free(header, M_SFXGE); return (rc); } map = stmp->map; txq->tso_long_headers++; stmp->u.heap_buf = header; stmp->flags = TX_BUF_UNMAP; } tsoh_th = (struct tcphdr *)(header + tso->tcph_off); /* Copy and update the headers. */ m_copydata(tso->mbuf, 0, tso->header_len, header); tsoh_th->th_seq = htonl(tso->seqnum); tso->seqnum += tso->seg_size; if (tso->out_len > tso->seg_size) { /* This packet will not finish the TSO burst. */ ip_length = tso->header_len - tso->nh_off + tso->seg_size; tsoh_th->th_flags &= ~(TH_FIN | TH_PUSH); } else { /* This packet will be the last in the TSO burst. */ ip_length = tso->header_len - tso->nh_off + tso->out_len; } if (tso->protocol == htons(ETHERTYPE_IP)) { struct ip *tsoh_iph = (struct ip *)(header + tso->nh_off); tsoh_iph->ip_len = htons(ip_length); /* XXX We should increment ip_id, but FreeBSD doesn't * currently allocate extra IDs for multiple segments. */ } else { struct ip6_hdr *tsoh_iph = (struct ip6_hdr *)(header + tso->nh_off); tsoh_iph->ip6_plen = htons(ip_length - sizeof(*tsoh_iph)); } /* Make the header visible to the hardware. */ bus_dmamap_sync(txq->packet_dma_tag, map, BUS_DMASYNC_PREWRITE); /* Form a descriptor for this header. */ desc = &txq->pend_desc[txq->n_pend_desc++]; efx_tx_qdesc_dma_create(txq->common, dma_addr, tso->header_len, 0, desc); id = (id + 1) & txq->ptr_mask; tso->segs_space = UINT_MAX; } tso->packet_space = tso->seg_size; txq->tso_packets++; *idp = id; return (0); } static int sfxge_tx_queue_tso(struct sfxge_txq *txq, struct mbuf *mbuf, const bus_dma_segment_t *dma_seg, int n_dma_seg, - int vlan_tagged) + int n_extra_descs) { struct sfxge_tso_state tso; unsigned int id; unsigned skipped = 0; tso_start(txq, &tso, dma_seg, mbuf); while (dma_seg->ds_len + skipped <= tso.header_len) { skipped += dma_seg->ds_len; --n_dma_seg; KASSERT(n_dma_seg, ("no payload found in TSO packet")); ++dma_seg; } tso.in_len = dma_seg->ds_len - (tso.header_len - skipped); tso.dma_addr = dma_seg->ds_addr + (tso.header_len - skipped); - id = (txq->added + vlan_tagged) & txq->ptr_mask; + id = (txq->added + n_extra_descs) & txq->ptr_mask; if (__predict_false(tso_start_new_packet(txq, &tso, &id))) return (-1); while (1) { tso_fill_packet_with_fragment(txq, &tso); /* Exactly one DMA descriptor is added */ KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0")); id = (id + 1) & txq->ptr_mask; /* Move onto the next fragment? */ if (tso.in_len == 0) { --n_dma_seg; if (n_dma_seg == 0) break; ++dma_seg; tso.in_len = dma_seg->ds_len; tso.dma_addr = dma_seg->ds_addr; } /* End of packet? */ if ((tso.packet_space == 0) | (tso.segs_space == 0)) { unsigned int n_fatso_opt_desc = (tso.fw_assisted & SFXGE_FATSOV2) ? EFX_TX_FATSOV2_OPT_NDESCS : (tso.fw_assisted & SFXGE_FATSOV1) ? 1 : 0; /* If the queue is now full due to tiny MSS, * or we can't create another header, discard * the remainder of the input mbuf but do not * roll back the work we have done. */ if (txq->n_pend_desc + n_fatso_opt_desc + 1 /* header */ + n_dma_seg > txq->max_pkt_desc) { txq->tso_pdrop_too_many++; break; } if (__predict_false(tso_start_new_packet(txq, &tso, &id))) { txq->tso_pdrop_no_rsrc++; break; } } } txq->tso_bursts++; return (id); } static void sfxge_tx_qunblock(struct sfxge_txq *txq) { struct sfxge_softc *sc; struct sfxge_evq *evq; sc = txq->sc; evq = sc->evq[txq->evq_index]; SFXGE_EVQ_LOCK_ASSERT_OWNED(evq); if (__predict_false(txq->init_state != SFXGE_TXQ_STARTED)) return; SFXGE_TXQ_LOCK(txq); if (txq->blocked) { unsigned int level; level = txq->added - txq->completed; if (level <= SFXGE_TXQ_UNBLOCK_LEVEL(txq->entries)) { /* reaped must be in sync with blocked */ sfxge_tx_qreap(txq); txq->blocked = 0; } } sfxge_tx_qdpl_service(txq); /* note: lock has been dropped */ } void sfxge_tx_qflush_done(struct sfxge_txq *txq) { txq->flush_state = SFXGE_FLUSH_DONE; } static void sfxge_tx_qstop(struct sfxge_softc *sc, unsigned int index) { struct sfxge_txq *txq; struct sfxge_evq *evq; unsigned int count; SFXGE_ADAPTER_LOCK_ASSERT_OWNED(sc); txq = sc->txq[index]; evq = sc->evq[txq->evq_index]; SFXGE_EVQ_LOCK(evq); SFXGE_TXQ_LOCK(txq); KASSERT(txq->init_state == SFXGE_TXQ_STARTED, ("txq->init_state != SFXGE_TXQ_STARTED")); txq->init_state = SFXGE_TXQ_INITIALIZED; if (txq->flush_state != SFXGE_FLUSH_DONE) { txq->flush_state = SFXGE_FLUSH_PENDING; SFXGE_EVQ_UNLOCK(evq); SFXGE_TXQ_UNLOCK(txq); /* Flush the transmit queue. */ if (efx_tx_qflush(txq->common) != 0) { log(LOG_ERR, "%s: Flushing Tx queue %u failed\n", device_get_nameunit(sc->dev), index); txq->flush_state = SFXGE_FLUSH_DONE; } else { count = 0; do { /* Spin for 100ms. */ DELAY(100000); if (txq->flush_state != SFXGE_FLUSH_PENDING) break; } while (++count < 20); } SFXGE_EVQ_LOCK(evq); SFXGE_TXQ_LOCK(txq); KASSERT(txq->flush_state != SFXGE_FLUSH_FAILED, ("txq->flush_state == SFXGE_FLUSH_FAILED")); if (txq->flush_state != SFXGE_FLUSH_DONE) { /* Flush timeout */ log(LOG_ERR, "%s: Cannot flush Tx queue %u\n", device_get_nameunit(sc->dev), index); txq->flush_state = SFXGE_FLUSH_DONE; } } txq->blocked = 0; txq->pending = txq->added; sfxge_tx_qcomplete(txq, evq); KASSERT(txq->completed == txq->added, ("txq->completed != txq->added")); sfxge_tx_qreap(txq); KASSERT(txq->reaped == txq->completed, ("txq->reaped != txq->completed")); txq->added = 0; txq->pending = 0; txq->completed = 0; txq->reaped = 0; /* Destroy the common code transmit queue. */ efx_tx_qdestroy(txq->common); txq->common = NULL; efx_sram_buf_tbl_clear(sc->enp, txq->buf_base_id, EFX_TXQ_NBUFS(sc->txq_entries)); + txq->hw_cksum_flags = 0; + SFXGE_EVQ_UNLOCK(evq); SFXGE_TXQ_UNLOCK(txq); } /* * Estimate maximum number of Tx descriptors required for TSO packet. * With minimum MSS and maximum mbuf length we might need more (even * than a ring-ful of descriptors), but this should not happen in * practice except due to deliberate attack. In that case we will * truncate the output at a packet boundary. */ static unsigned int sfxge_tx_max_pkt_desc(const struct sfxge_softc *sc, enum sfxge_txq_type type, unsigned int tso_fw_assisted) { /* One descriptor for every input fragment */ unsigned int max_descs = SFXGE_TX_MAPPING_MAX_SEG; unsigned int sw_tso_max_descs; unsigned int fa_tso_v1_max_descs = 0; unsigned int fa_tso_v2_max_descs = 0; + /* Checksum offload Tx option descriptor may be required */ + if (sc->txq_dynamic_cksum_toggle_supported) + max_descs++; + /* VLAN tagging Tx option descriptor may be required */ if (efx_nic_cfg_get(sc->enp)->enc_hw_tx_insert_vlan_enabled) max_descs++; if (type == SFXGE_TXQ_IP_TCP_UDP_CKSUM) { /* * Plus header and payload descriptor for each output segment. * Minus one since header fragment is already counted. * Even if FATSO is used, we should be ready to fallback * to do it in the driver. */ sw_tso_max_descs = SFXGE_TSO_MAX_SEGS * 2 - 1; /* FW assisted TSOv1 requires one more descriptor per segment * in comparison to SW TSO */ if (tso_fw_assisted & SFXGE_FATSOV1) fa_tso_v1_max_descs = sw_tso_max_descs + SFXGE_TSO_MAX_SEGS; /* FW assisted TSOv2 requires 3 (2 FATSO plus header) extra * descriptors per superframe limited by number of DMA fetches * per packet. The first packet header is already counted. */ if (tso_fw_assisted & SFXGE_FATSOV2) { fa_tso_v2_max_descs = howmany(SFXGE_TX_MAPPING_MAX_SEG, EFX_TX_FATSOV2_DMA_SEGS_PER_PKT_MAX - 1) * (EFX_TX_FATSOV2_OPT_NDESCS + 1) - 1; } max_descs += MAX(sw_tso_max_descs, MAX(fa_tso_v1_max_descs, fa_tso_v2_max_descs)); } return (max_descs); } static int sfxge_tx_qstart(struct sfxge_softc *sc, unsigned int index) { struct sfxge_txq *txq; efsys_mem_t *esmp; uint16_t flags; unsigned int tso_fw_assisted; + unsigned int label; struct sfxge_evq *evq; unsigned int desc_index; int rc; SFXGE_ADAPTER_LOCK_ASSERT_OWNED(sc); txq = sc->txq[index]; esmp = &txq->mem; evq = sc->evq[txq->evq_index]; KASSERT(txq->init_state == SFXGE_TXQ_INITIALIZED, ("txq->init_state != SFXGE_TXQ_INITIALIZED")); KASSERT(evq->init_state == SFXGE_EVQ_STARTED, ("evq->init_state != SFXGE_EVQ_STARTED")); /* Program the buffer table. */ if ((rc = efx_sram_buf_tbl_set(sc->enp, txq->buf_base_id, esmp, EFX_TXQ_NBUFS(sc->txq_entries))) != 0) return (rc); /* Determine the kind of queue we are creating. */ tso_fw_assisted = 0; switch (txq->type) { case SFXGE_TXQ_NON_CKSUM: flags = 0; break; case SFXGE_TXQ_IP_CKSUM: flags = EFX_TXQ_CKSUM_IPV4; break; case SFXGE_TXQ_IP_TCP_UDP_CKSUM: flags = EFX_TXQ_CKSUM_IPV4 | EFX_TXQ_CKSUM_TCPUDP; tso_fw_assisted = sc->tso_fw_assisted; if (tso_fw_assisted & SFXGE_FATSOV2) flags |= EFX_TXQ_FATSOV2; break; default: KASSERT(0, ("Impossible TX queue")); flags = 0; break; } + label = (sc->txq_dynamic_cksum_toggle_supported) ? 0 : txq->type; + /* Create the common code transmit queue. */ - if ((rc = efx_tx_qcreate(sc->enp, index, txq->type, esmp, + if ((rc = efx_tx_qcreate(sc->enp, index, label, esmp, sc->txq_entries, txq->buf_base_id, flags, evq->common, &txq->common, &desc_index)) != 0) { /* Retry if no FATSOv2 resources, otherwise fail */ if ((rc != ENOSPC) || (~flags & EFX_TXQ_FATSOV2)) goto fail; /* Looks like all FATSOv2 contexts are used */ flags &= ~EFX_TXQ_FATSOV2; tso_fw_assisted &= ~SFXGE_FATSOV2; - if ((rc = efx_tx_qcreate(sc->enp, index, txq->type, esmp, + if ((rc = efx_tx_qcreate(sc->enp, index, label, esmp, sc->txq_entries, txq->buf_base_id, flags, evq->common, &txq->common, &desc_index)) != 0) goto fail; } /* Initialise queue descriptor indexes */ txq->added = txq->pending = txq->completed = txq->reaped = desc_index; SFXGE_TXQ_LOCK(txq); /* Enable the transmit queue. */ efx_tx_qenable(txq->common); txq->init_state = SFXGE_TXQ_STARTED; txq->flush_state = SFXGE_FLUSH_REQUIRED; txq->tso_fw_assisted = tso_fw_assisted; txq->max_pkt_desc = sfxge_tx_max_pkt_desc(sc, txq->type, tso_fw_assisted); txq->hw_vlan_tci = 0; + txq->hw_cksum_flags = flags & + (EFX_TXQ_CKSUM_IPV4 | EFX_TXQ_CKSUM_TCPUDP); + SFXGE_TXQ_UNLOCK(txq); return (0); fail: efx_sram_buf_tbl_clear(sc->enp, txq->buf_base_id, EFX_TXQ_NBUFS(sc->txq_entries)); return (rc); } void sfxge_tx_stop(struct sfxge_softc *sc) { int index; index = sc->txq_count; while (--index >= 0) sfxge_tx_qstop(sc, index); /* Tear down the transmit module */ efx_tx_fini(sc->enp); } int sfxge_tx_start(struct sfxge_softc *sc) { int index; int rc; /* Initialize the common code transmit module. */ if ((rc = efx_tx_init(sc->enp)) != 0) return (rc); for (index = 0; index < sc->txq_count; index++) { if ((rc = sfxge_tx_qstart(sc, index)) != 0) goto fail; } return (0); fail: while (--index >= 0) sfxge_tx_qstop(sc, index); efx_tx_fini(sc->enp); return (rc); } static int sfxge_txq_stat_init(struct sfxge_txq *txq, struct sysctl_oid *txq_node) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(txq->sc->dev); struct sysctl_oid *stat_node; unsigned int id; stat_node = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(txq_node), OID_AUTO, "stats", CTLFLAG_RD, NULL, "Tx queue statistics"); if (stat_node == NULL) return (ENOMEM); for (id = 0; id < nitems(sfxge_tx_stats); id++) { SYSCTL_ADD_ULONG( ctx, SYSCTL_CHILDREN(stat_node), OID_AUTO, sfxge_tx_stats[id].name, CTLFLAG_RD | CTLFLAG_STATS, (unsigned long *)((caddr_t)txq + sfxge_tx_stats[id].offset), ""); } return (0); } /** * Destroy a transmit queue. */ static void sfxge_tx_qfini(struct sfxge_softc *sc, unsigned int index) { struct sfxge_txq *txq; unsigned int nmaps; txq = sc->txq[index]; KASSERT(txq->init_state == SFXGE_TXQ_INITIALIZED, ("txq->init_state != SFXGE_TXQ_INITIALIZED")); if (txq->type == SFXGE_TXQ_IP_TCP_UDP_CKSUM) tso_fini(txq); /* Free the context arrays. */ free(txq->pend_desc, M_SFXGE); nmaps = sc->txq_entries; while (nmaps-- != 0) bus_dmamap_destroy(txq->packet_dma_tag, txq->stmp[nmaps].map); free(txq->stmp, M_SFXGE); /* Release DMA memory mapping. */ sfxge_dma_free(&txq->mem); sc->txq[index] = NULL; SFXGE_TXQ_LOCK_DESTROY(txq); free(txq, M_SFXGE); } static int sfxge_tx_qinit(struct sfxge_softc *sc, unsigned int txq_index, enum sfxge_txq_type type, unsigned int evq_index) { const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp); char name[16]; struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); struct sysctl_oid *txq_node; struct sfxge_txq *txq; struct sfxge_evq *evq; struct sfxge_tx_dpl *stdp; struct sysctl_oid *dpl_node; efsys_mem_t *esmp; unsigned int nmaps; int rc; txq = malloc(sizeof(struct sfxge_txq), M_SFXGE, M_ZERO | M_WAITOK); txq->sc = sc; txq->entries = sc->txq_entries; txq->ptr_mask = txq->entries - 1; sc->txq[txq_index] = txq; esmp = &txq->mem; evq = sc->evq[evq_index]; /* Allocate and zero DMA space for the descriptor ring. */ if ((rc = sfxge_dma_alloc(sc, EFX_TXQ_SIZE(sc->txq_entries), esmp)) != 0) return (rc); /* Allocate buffer table entries. */ sfxge_sram_buf_tbl_alloc(sc, EFX_TXQ_NBUFS(sc->txq_entries), &txq->buf_base_id); /* Create a DMA tag for packet mappings. */ if (bus_dma_tag_create(sc->parent_dma_tag, 1, encp->enc_tx_dma_desc_boundary, MIN(0x3FFFFFFFFFFFUL, BUS_SPACE_MAXADDR), BUS_SPACE_MAXADDR, NULL, NULL, 0x11000, SFXGE_TX_MAPPING_MAX_SEG, encp->enc_tx_dma_desc_size_max, 0, NULL, NULL, &txq->packet_dma_tag) != 0) { device_printf(sc->dev, "Couldn't allocate txq DMA tag\n"); rc = ENOMEM; goto fail; } /* Allocate pending descriptor array for batching writes. */ txq->pend_desc = malloc(sizeof(efx_desc_t) * sc->txq_entries, M_SFXGE, M_ZERO | M_WAITOK); /* Allocate and initialise mbuf DMA mapping array. */ txq->stmp = malloc(sizeof(struct sfxge_tx_mapping) * sc->txq_entries, M_SFXGE, M_ZERO | M_WAITOK); for (nmaps = 0; nmaps < sc->txq_entries; nmaps++) { rc = bus_dmamap_create(txq->packet_dma_tag, 0, &txq->stmp[nmaps].map); if (rc != 0) goto fail2; } snprintf(name, sizeof(name), "%u", txq_index); txq_node = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sc->txqs_node), OID_AUTO, name, CTLFLAG_RD, NULL, ""); if (txq_node == NULL) { rc = ENOMEM; goto fail_txq_node; } if (type == SFXGE_TXQ_IP_TCP_UDP_CKSUM && (rc = tso_init(txq)) != 0) goto fail3; /* Initialize the deferred packet list. */ stdp = &txq->dpl; stdp->std_put_max = sfxge_tx_dpl_put_max; stdp->std_get_max = sfxge_tx_dpl_get_max; stdp->std_get_non_tcp_max = sfxge_tx_dpl_get_non_tcp_max; stdp->std_getp = &stdp->std_get; SFXGE_TXQ_LOCK_INIT(txq, device_get_nameunit(sc->dev), txq_index); dpl_node = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(txq_node), OID_AUTO, "dpl", CTLFLAG_RD, NULL, "Deferred packet list statistics"); if (dpl_node == NULL) { rc = ENOMEM; goto fail_dpl_node; } SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(dpl_node), OID_AUTO, "get_count", CTLFLAG_RD | CTLFLAG_STATS, &stdp->std_get_count, 0, ""); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(dpl_node), OID_AUTO, "get_non_tcp_count", CTLFLAG_RD | CTLFLAG_STATS, &stdp->std_get_non_tcp_count, 0, ""); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(dpl_node), OID_AUTO, "get_hiwat", CTLFLAG_RD | CTLFLAG_STATS, &stdp->std_get_hiwat, 0, ""); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(dpl_node), OID_AUTO, "put_hiwat", CTLFLAG_RD | CTLFLAG_STATS, &stdp->std_put_hiwat, 0, ""); rc = sfxge_txq_stat_init(txq, txq_node); if (rc != 0) goto fail_txq_stat_init; txq->type = type; txq->evq_index = evq_index; txq->init_state = SFXGE_TXQ_INITIALIZED; return (0); fail_txq_stat_init: fail_dpl_node: fail3: fail_txq_node: free(txq->pend_desc, M_SFXGE); fail2: while (nmaps-- != 0) bus_dmamap_destroy(txq->packet_dma_tag, txq->stmp[nmaps].map); free(txq->stmp, M_SFXGE); bus_dma_tag_destroy(txq->packet_dma_tag); fail: sfxge_dma_free(esmp); return (rc); } static int sfxge_tx_stat_handler(SYSCTL_HANDLER_ARGS) { struct sfxge_softc *sc = arg1; unsigned int id = arg2; unsigned long sum; unsigned int index; /* Sum across all TX queues */ sum = 0; for (index = 0; index < sc->txq_count; index++) sum += *(unsigned long *)((caddr_t)sc->txq[index] + sfxge_tx_stats[id].offset); return (SYSCTL_OUT(req, &sum, sizeof(sum))); } static void sfxge_tx_stat_init(struct sfxge_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); struct sysctl_oid_list *stat_list; unsigned int id; stat_list = SYSCTL_CHILDREN(sc->stats_node); for (id = 0; id < nitems(sfxge_tx_stats); id++) { SYSCTL_ADD_PROC( ctx, stat_list, OID_AUTO, sfxge_tx_stats[id].name, CTLTYPE_ULONG|CTLFLAG_RD, sc, id, sfxge_tx_stat_handler, "LU", ""); } } uint64_t sfxge_tx_get_drops(struct sfxge_softc *sc) { unsigned int index; uint64_t drops = 0; struct sfxge_txq *txq; /* Sum across all TX queues */ for (index = 0; index < sc->txq_count; index++) { txq = sc->txq[index]; /* * In theory, txq->put_overflow and txq->netdown_drops * should use atomic operation and other should be * obtained under txq lock, but it is just statistics. */ drops += txq->drops + txq->get_overflow + txq->get_non_tcp_overflow + txq->put_overflow + txq->netdown_drops + txq->tso_pdrop_too_many + txq->tso_pdrop_no_rsrc; } return (drops); } void sfxge_tx_fini(struct sfxge_softc *sc) { int index; index = sc->txq_count; while (--index >= 0) sfxge_tx_qfini(sc, index); sc->txq_count = 0; } int sfxge_tx_init(struct sfxge_softc *sc) { const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp); struct sfxge_intr *intr; int index; int rc; intr = &sc->intr; KASSERT(intr->state == SFXGE_INTR_INITIALIZED, ("intr->state != SFXGE_INTR_INITIALIZED")); if (sfxge_tx_dpl_get_max <= 0) { log(LOG_ERR, "%s=%d must be greater than 0", SFXGE_PARAM_TX_DPL_GET_MAX, sfxge_tx_dpl_get_max); rc = EINVAL; goto fail_tx_dpl_get_max; } if (sfxge_tx_dpl_get_non_tcp_max <= 0) { log(LOG_ERR, "%s=%d must be greater than 0", SFXGE_PARAM_TX_DPL_GET_NON_TCP_MAX, sfxge_tx_dpl_get_non_tcp_max); rc = EINVAL; goto fail_tx_dpl_get_non_tcp_max; } if (sfxge_tx_dpl_put_max < 0) { log(LOG_ERR, "%s=%d must be greater or equal to 0", SFXGE_PARAM_TX_DPL_PUT_MAX, sfxge_tx_dpl_put_max); rc = EINVAL; goto fail_tx_dpl_put_max; } - sc->txq_count = SFXGE_TXQ_NTYPES - 1 + sc->intr.n_alloc; + sc->txq_count = SFXGE_EVQ0_N_TXQ(sc) - 1 + sc->intr.n_alloc; sc->tso_fw_assisted = sfxge_tso_fw_assisted; if ((~encp->enc_features & EFX_FEATURE_FW_ASSISTED_TSO) || (!encp->enc_fw_assisted_tso_enabled)) sc->tso_fw_assisted &= ~SFXGE_FATSOV1; if ((~encp->enc_features & EFX_FEATURE_FW_ASSISTED_TSO_V2) || (!encp->enc_fw_assisted_tso_v2_enabled)) sc->tso_fw_assisted &= ~SFXGE_FATSOV2; sc->txqs_node = SYSCTL_ADD_NODE( device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "txq", CTLFLAG_RD, NULL, "Tx queues"); if (sc->txqs_node == NULL) { rc = ENOMEM; goto fail_txq_node; } /* Initialize the transmit queues */ - if ((rc = sfxge_tx_qinit(sc, SFXGE_TXQ_NON_CKSUM, - SFXGE_TXQ_NON_CKSUM, 0)) != 0) - goto fail; + if (sc->txq_dynamic_cksum_toggle_supported == B_FALSE) { + if ((rc = sfxge_tx_qinit(sc, SFXGE_TXQ_NON_CKSUM, + SFXGE_TXQ_NON_CKSUM, 0)) != 0) + goto fail; - if ((rc = sfxge_tx_qinit(sc, SFXGE_TXQ_IP_CKSUM, - SFXGE_TXQ_IP_CKSUM, 0)) != 0) - goto fail2; + if ((rc = sfxge_tx_qinit(sc, SFXGE_TXQ_IP_CKSUM, + SFXGE_TXQ_IP_CKSUM, 0)) != 0) + goto fail2; + } for (index = 0; - index < sc->txq_count - SFXGE_TXQ_NTYPES + 1; + index < sc->txq_count - SFXGE_EVQ0_N_TXQ(sc) + 1; index++) { - if ((rc = sfxge_tx_qinit(sc, SFXGE_TXQ_NTYPES - 1 + index, + if ((rc = sfxge_tx_qinit(sc, SFXGE_EVQ0_N_TXQ(sc) - 1 + index, SFXGE_TXQ_IP_TCP_UDP_CKSUM, index)) != 0) goto fail3; } sfxge_tx_stat_init(sc); return (0); fail3: while (--index >= 0) sfxge_tx_qfini(sc, SFXGE_TXQ_IP_TCP_UDP_CKSUM + index); sfxge_tx_qfini(sc, SFXGE_TXQ_IP_CKSUM); fail2: sfxge_tx_qfini(sc, SFXGE_TXQ_NON_CKSUM); fail: fail_txq_node: sc->txq_count = 0; fail_tx_dpl_put_max: fail_tx_dpl_get_non_tcp_max: fail_tx_dpl_get_max: return (rc); } Index: projects/clang700-import/sys/dev/sfxge/sfxge_tx.h =================================================================== --- projects/clang700-import/sys/dev/sfxge/sfxge_tx.h (revision 341812) +++ projects/clang700-import/sys/dev/sfxge/sfxge_tx.h (revision 341813) @@ -1,245 +1,252 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010-2016 Solarflare Communications Inc. * All rights reserved. * * This software was developed in part by Philip Paeps under contract for * Solarflare Communications, 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. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the FreeBSD Project. * * $FreeBSD$ */ #ifndef _SFXGE_TX_H #define _SFXGE_TX_H #include #include #include /* If defined, parse TX packets directly in if_transmit * for better cache locality and reduced time under TX lock */ #define SFXGE_TX_PARSE_EARLY 1 /* Maximum size of TSO packet */ #define SFXGE_TSO_MAX_SIZE (65535) /* * Maximum number of segments to be created for a TSO packet. * Allow for a reasonable minimum MSS of 512. */ #define SFXGE_TSO_MAX_SEGS howmany(SFXGE_TSO_MAX_SIZE, 512) /* Maximum number of DMA segments needed to map an mbuf chain. With * TSO, the mbuf length may be just over 64K, divided into 2K mbuf * clusters taking into account that the first may be not 2K cluster * boundary aligned. * Packet header may be split into two segments because of, for example, * VLAN header insertion. * The chain could be longer than this initially, but can be shortened * with m_collapse(). */ #define SFXGE_TX_MAPPING_MAX_SEG \ (2 + howmany(SFXGE_TSO_MAX_SIZE, MCLBYTES) + 1) /* * Buffer mapping flags. * * Buffers and DMA mappings must be freed when the last descriptor * referring to them is completed. Set the TX_BUF_UNMAP and * TX_BUF_MBUF flags on the last descriptor generated for an mbuf * chain. Set only the TX_BUF_UNMAP flag on a descriptor referring to * a heap buffer. */ enum sfxge_tx_buf_flags { TX_BUF_UNMAP = 1, TX_BUF_MBUF = 2, }; /* * Buffer mapping information for descriptors in flight. */ struct sfxge_tx_mapping { union { struct mbuf *mbuf; caddr_t heap_buf; } u; bus_dmamap_t map; enum sfxge_tx_buf_flags flags; }; #define SFXGE_TX_DPL_GET_PKT_LIMIT_DEFAULT (64 * 1024) #define SFXGE_TX_DPL_GET_NON_TCP_PKT_LIMIT_DEFAULT 1024 #define SFXGE_TX_DPL_PUT_PKT_LIMIT_DEFAULT 1024 /* * Deferred packet list. */ struct sfxge_tx_dpl { unsigned int std_get_max; /* Maximum number of packets * in get list */ unsigned int std_get_non_tcp_max; /* Maximum number * of non-TCP packets * in get list */ unsigned int std_put_max; /* Maximum number of packets * in put list */ uintptr_t std_put; /* Head of put list. */ struct mbuf *std_get; /* Head of get list. */ struct mbuf **std_getp; /* Tail of get list. */ unsigned int std_get_count; /* Packets in get list. */ unsigned int std_get_non_tcp_count; /* Non-TCP packets * in get list */ unsigned int std_get_hiwat; /* Packets in get list * high watermark */ unsigned int std_put_hiwat; /* Packets in put list * high watermark */ }; #define SFXGE_TX_BUFFER_SIZE 0x400 #define SFXGE_TX_HEADER_SIZE 0x100 #define SFXGE_TX_COPY_THRESHOLD 0x200 enum sfxge_txq_state { SFXGE_TXQ_UNINITIALIZED = 0, SFXGE_TXQ_INITIALIZED, SFXGE_TXQ_STARTED }; enum sfxge_txq_type { SFXGE_TXQ_NON_CKSUM = 0, SFXGE_TXQ_IP_CKSUM, SFXGE_TXQ_IP_TCP_UDP_CKSUM, SFXGE_TXQ_NTYPES }; +#define SFXGE_EVQ0_N_TXQ(_sc) \ + ((_sc)->txq_dynamic_cksum_toggle_supported ? \ + 1 : SFXGE_TXQ_NTYPES) + #define SFXGE_TXQ_UNBLOCK_LEVEL(_entries) (EFX_TXQ_LIMIT(_entries) / 4) #define SFXGE_TX_BATCH 64 #define SFXGE_TXQ_LOCK_INIT(_txq, _ifname, _txq_index) \ do { \ struct sfxge_txq *__txq = (_txq); \ \ snprintf((__txq)->lock_name, \ sizeof((__txq)->lock_name), \ "%s:txq%u", (_ifname), (_txq_index)); \ mtx_init(&(__txq)->lock, (__txq)->lock_name, \ NULL, MTX_DEF); \ } while (B_FALSE) #define SFXGE_TXQ_LOCK_DESTROY(_txq) \ mtx_destroy(&(_txq)->lock) #define SFXGE_TXQ_LOCK(_txq) \ mtx_lock(&(_txq)->lock) #define SFXGE_TXQ_TRYLOCK(_txq) \ mtx_trylock(&(_txq)->lock) #define SFXGE_TXQ_UNLOCK(_txq) \ mtx_unlock(&(_txq)->lock) #define SFXGE_TXQ_LOCK_ASSERT_OWNED(_txq) \ mtx_assert(&(_txq)->lock, MA_OWNED) #define SFXGE_TXQ_LOCK_ASSERT_NOTOWNED(_txq) \ mtx_assert(&(_txq)->lock, MA_NOTOWNED) struct sfxge_txq { /* The following fields should be written very rarely */ struct sfxge_softc *sc; enum sfxge_txq_state init_state; enum sfxge_flush_state flush_state; unsigned int tso_fw_assisted; enum sfxge_txq_type type; unsigned int evq_index; efsys_mem_t mem; unsigned int buf_base_id; unsigned int entries; unsigned int ptr_mask; unsigned int max_pkt_desc; struct sfxge_tx_mapping *stmp; /* Packets in flight. */ bus_dma_tag_t packet_dma_tag; efx_desc_t *pend_desc; efx_txq_t *common; efsys_mem_t *tsoh_buffer; char lock_name[SFXGE_LOCK_NAME_MAX]; /* This field changes more often and is read regularly on both * the initiation and completion paths */ int blocked __aligned(CACHE_LINE_SIZE); /* The following fields change more often, and are used mostly * on the initiation path */ struct mtx lock __aligned(CACHE_LINE_SIZE); struct sfxge_tx_dpl dpl; /* Deferred packet list. */ unsigned int n_pend_desc; unsigned int added; unsigned int reaped; + + /* The last (or constant) set of HW offloads requested on the queue */ + uint16_t hw_cksum_flags; /* The last VLAN TCI seen on the queue if FW-assisted tagging is used */ uint16_t hw_vlan_tci; /* Statistics */ unsigned long tso_bursts; unsigned long tso_packets; unsigned long tso_long_headers; unsigned long collapses; unsigned long drops; unsigned long get_overflow; unsigned long get_non_tcp_overflow; unsigned long put_overflow; unsigned long netdown_drops; unsigned long tso_pdrop_too_many; unsigned long tso_pdrop_no_rsrc; /* The following fields change more often, and are used mostly * on the completion path */ unsigned int pending __aligned(CACHE_LINE_SIZE); unsigned int completed; struct sfxge_txq *next; }; struct sfxge_evq; extern uint64_t sfxge_tx_get_drops(struct sfxge_softc *sc); extern int sfxge_tx_init(struct sfxge_softc *sc); extern void sfxge_tx_fini(struct sfxge_softc *sc); extern int sfxge_tx_start(struct sfxge_softc *sc); extern void sfxge_tx_stop(struct sfxge_softc *sc); extern void sfxge_tx_qcomplete(struct sfxge_txq *txq, struct sfxge_evq *evq); extern void sfxge_tx_qflush_done(struct sfxge_txq *txq); extern void sfxge_if_qflush(struct ifnet *ifp); extern int sfxge_if_transmit(struct ifnet *ifp, struct mbuf *m); #endif Index: projects/clang700-import/sys/dev/usb/usbdevs =================================================================== --- projects/clang700-import/sys/dev/usb/usbdevs (revision 341812) +++ projects/clang700-import/sys/dev/usb/usbdevs (revision 341813) @@ -1,4902 +1,4904 @@ $FreeBSD$ /* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */ /*- * Copyright (c) 1998-2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * List of known USB vendors * * USB.org publishes a VID list of USB-IF member companies at * http://www.usb.org/developers/tools * Note that it does not show companies that have obtained a Vendor ID * without becoming full members. * * Please note that these IDs do not do anything. Adding an ID here and * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name * available to the source code and does not change any functionality, nor * does it make your device available to a specific driver. * It will however make the descriptive string available if a device does not * provide the string itself. * * After adding a vendor ID VNDR and a product ID PRDCT you will have the * following extra defines: * #define USB_VENDOR_VNDR 0x???? * #define USB_PRODUCT_VNDR_PRDCT 0x???? * * You may have to add these defines to the respective probe routines to * make the device recognised by the appropriate device driver. */ vendor UNKNOWN1 0x0053 Unknown vendor vendor UNKNOWN2 0x0105 Unknown vendor vendor EGALAX2 0x0123 eGalax, Inc. vendor CHIPSBANK 0x0204 Chipsbank Microelectronics Co. vendor HUMAX 0x02ad HUMAX vendor QUAN 0x01e1 Quan vendor LTS 0x0386 LTS vendor BWCT 0x03da Bernd Walter Computer Technology vendor AOX 0x03e8 AOX vendor THESYS 0x03e9 Thesys vendor DATABROADCAST 0x03ea Data Broadcasting vendor ATMEL 0x03eb Atmel vendor IWATSU 0x03ec Iwatsu America vendor MITSUMI 0x03ee Mitsumi vendor HP 0x03f0 Hewlett Packard vendor GENOA 0x03f1 Genoa vendor OAK 0x03f2 Oak vendor ADAPTEC 0x03f3 Adaptec vendor DIEBOLD 0x03f4 Diebold vendor SIEMENSELECTRO 0x03f5 Siemens Electromechanical vendor EPSONIMAGING 0x03f8 Epson Imaging vendor KEYTRONIC 0x03f9 KeyTronic vendor OPTI 0x03fb OPTi vendor ELITEGROUP 0x03fc Elitegroup vendor XILINX 0x03fd Xilinx vendor FARALLON 0x03fe Farallon Communications vendor NATIONAL 0x0400 National Semiconductor vendor NATIONALREG 0x0401 National Registry vendor ACERLABS 0x0402 Acer Labs vendor FTDI 0x0403 Future Technology Devices vendor NCR 0x0404 NCR vendor SYNOPSYS2 0x0405 Synopsys vendor FUJITSUICL 0x0406 Fujitsu-ICL vendor FUJITSU2 0x0407 Fujitsu Personal Systems vendor QUANTA 0x0408 Quanta vendor NEC 0x0409 NEC vendor KODAK 0x040a Eastman Kodak vendor WELTREND 0x040b Weltrend Semiconductor vendor VIA 0x040d VIA vendor MCCI 0x040e MCCI vendor MELCO 0x0411 Melco vendor LEADTEK 0x0413 Leadtek vendor WINBOND 0x0416 Winbond vendor PHOENIX 0x041a Phoenix vendor CREATIVE 0x041e Creative Labs vendor NOKIA 0x0421 Nokia vendor ADI 0x0422 ADI Systems vendor CATC 0x0423 Computer Access Technology vendor SMC2 0x0424 Microchip (Standard Microsystems) vendor MOTOROLA_HK 0x0425 Motorola HK vendor GRAVIS 0x0428 Advanced Gravis Computer vendor CIRRUSLOGIC 0x0429 Cirrus Logic vendor INNOVATIVE 0x042c Innovative Semiconductors vendor MOLEX 0x042f Molex vendor SUN 0x0430 Sun Microsystems vendor UNISYS 0x0432 Unisys vendor TAUGA 0x0436 Taugagreining HF vendor AMD 0x0438 Advanced Micro Devices vendor LEXMARK 0x043d Lexmark International vendor LG 0x043e LG Electronics vendor NANAO 0x0440 NANAO vendor GATEWAY 0x0443 Gateway 2000 vendor NMB 0x0446 NMB vendor ALPS 0x044e Alps Electric vendor THRUST 0x044f Thrustmaster vendor TI 0x0451 Texas Instruments vendor ANALOGDEVICES 0x0456 Analog Devices vendor SIS 0x0457 Silicon Integrated Systems Corp. vendor KYE 0x0458 KYE Systems vendor DIAMOND2 0x045a Diamond (Supra) vendor RENESAS 0x045b Renesas vendor MICROSOFT 0x045e Microsoft vendor PRIMAX 0x0461 Primax Electronics vendor MGE 0x0463 MGE UPS Systems vendor AMP 0x0464 AMP vendor CHERRY 0x046a Cherry Mikroschalter vendor MEGATRENDS 0x046b American Megatrends vendor LOGITECH 0x046d Logitech vendor BTC 0x046e Behavior Tech. Computer vendor PHILIPS 0x0471 Philips vendor SUN2 0x0472 Sun Microsystems (official) vendor SANYO 0x0474 Sanyo Electric vendor SEAGATE 0x0477 Seagate vendor CONNECTIX 0x0478 Connectix vendor SEMTECH 0x047a Semtech vendor KENSINGTON 0x047d Kensington vendor LUCENT 0x047e Lucent vendor PLANTRONICS 0x047f Plantronics vendor KYOCERA 0x0482 Kyocera Wireless Corp. vendor STMICRO 0x0483 STMicroelectronics vendor FOXCONN 0x0489 Foxconn / Hon Hai vendor MEIZU 0x0492 Meizu Electronics vendor YAMAHA 0x0499 YAMAHA vendor COMPAQ 0x049f Compaq vendor HITACHI 0x04a4 Hitachi vendor ACERP 0x04a5 Acer Peripherals vendor DAVICOM 0x04a6 Davicom vendor VISIONEER 0x04a7 Visioneer vendor CANON 0x04a9 Canon vendor NIKON 0x04b0 Nikon vendor PAN 0x04b1 Pan International vendor IBM 0x04b3 IBM vendor CYPRESS 0x04b4 Cypress Semiconductor vendor ROHM 0x04b5 ROHM vendor COMPAL 0x04b7 Compal vendor EPSON 0x04b8 Seiko Epson vendor RAINBOW 0x04b9 Rainbow Technologies vendor IODATA 0x04bb I-O Data vendor TDK 0x04bf TDK vendor 3COMUSR 0x04c1 U.S. Robotics vendor METHODE 0x04c2 Methode Electronics Far East vendor MAXISWITCH 0x04c3 Maxi Switch vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research vendor FUJITSU 0x04c5 Fujitsu vendor TOSHIBAAM 0x04c6 Toshiba America vendor MICROMACRO 0x04c7 Micro Macro Technologies vendor KONICA 0x04c8 Konica vendor LITEON 0x04ca Lite-On Technology vendor FUJIPHOTO 0x04cb Fuji Photo Film vendor PHILIPSSEMI 0x04cc Philips Semiconductors vendor TATUNG 0x04cd Tatung Co. Of America vendor SCANLOGIC 0x04ce ScanLogic vendor MYSON 0x04cf Myson Technology vendor DIGI2 0x04d0 Digi vendor ITTCANON 0x04d1 ITT Canon vendor ALTEC 0x04d2 Altec Lansing vendor LSI 0x04d4 LSI vendor MENTORGRAPHICS 0x04d6 Mentor Graphics vendor ITUNERNET 0x04d8 I-Tuner Networks vendor HOLTEK 0x04d9 Holtek Semiconductor, Inc. vendor PANASONIC 0x04da Panasonic (Matsushita) vendor HUANHSIN 0x04dc Huan Hsin vendor SHARP 0x04dd Sharp vendor IIYAMA 0x04e1 Iiyama vendor SHUTTLE 0x04e6 Shuttle Technology vendor ELO 0x04e7 Elo TouchSystems vendor SAMSUNG 0x04e8 Samsung Electronics vendor NORTHSTAR 0x04eb Northstar vendor TOKYOELECTRON 0x04ec Tokyo Electron vendor ANNABOOKS 0x04ed Annabooks vendor JVC 0x04f1 JVC vendor CHICONY 0x04f2 Chicony Electronics vendor ELAN 0x04f3 ELAN Microelectronics vendor NEWNEX 0x04f7 Newnex vendor BROTHER 0x04f9 Brother Industries vendor DALLAS 0x04fa Dallas Semiconductor vendor AIPTEK2 0x04fc AIPTEK International vendor PFU 0x04fe PFU vendor FUJIKURA 0x0501 Fujikura/DDK vendor ACER 0x0502 Acer vendor 3COM 0x0506 3Com vendor HOSIDEN 0x0507 Hosiden Corporation vendor AZTECH 0x0509 Aztech Systems vendor BELKIN 0x050d Belkin Components vendor KAWATSU 0x050f Kawatsu Semiconductor vendor FCI 0x0514 FCI vendor LONGWELL 0x0516 Longwell vendor COMPOSITE 0x0518 Composite vendor STAR 0x0519 Star Micronics vendor APC 0x051d American Power Conversion vendor SCIATLANTA 0x051e Scientific Atlanta vendor TSM 0x0520 TSM vendor CONNECTEK 0x0522 Advanced Connectek USA vendor NETCHIP 0x0525 NetChip Technology vendor ALTRA 0x0527 ALTRA vendor ATI 0x0528 ATI Technologies vendor AKS 0x0529 Aladdin Knowledge Systems vendor TEKOM 0x052b Tekom vendor CANONDEV 0x052c Canon vendor WACOMTECH 0x0531 Wacom vendor INVENTEC 0x0537 Inventec vendor SHYHSHIUN 0x0539 Shyh Shiun Terminals vendor PREHWERKE 0x053a Preh Werke Gmbh & Co. KG vendor SYNOPSYS 0x053f Synopsys vendor UNIACCESS 0x0540 Universal Access vendor VIEWSONIC 0x0543 ViewSonic vendor XIRLINK 0x0545 Xirlink vendor ANCHOR 0x0547 Anchor Chips vendor SONY 0x054c Sony vendor FUJIXEROX 0x0550 Fuji Xerox vendor VISION 0x0553 VLSI Vision vendor ASAHIKASEI 0x0556 Asahi Kasei Microsystems vendor ATEN 0x0557 ATEN International vendor SAMSUNG2 0x055d Samsung Electronics vendor MUSTEK 0x055f Mustek Systems vendor TELEX 0x0562 Telex Communications vendor CHINON 0x0564 Chinon vendor PERACOM 0x0565 Peracom Networks vendor ALCOR2 0x0566 Alcor Micro vendor XYRATEX 0x0567 Xyratex vendor WACOM 0x056a WACOM vendor ETEK 0x056c e-TEK Labs vendor EIZO 0x056d EIZO vendor ELECOM 0x056e Elecom vendor CONEXANT 0x0572 Conexant vendor HAUPPAUGE 0x0573 Hauppauge Computer Works vendor BAFO 0x0576 BAFO/Quality Computer Accessories vendor YEDATA 0x057b Y-E Data vendor AVM 0x057c AVM vendor NINTENDO 0x057e Nintendo vendor QUICKSHOT 0x057f Quickshot vendor ROLAND 0x0582 Roland vendor ROCKFIRE 0x0583 Rockfire vendor RATOC 0x0584 RATOC Systems vendor ZYXEL 0x0586 ZyXEL Communication vendor INFINEON 0x058b Infineon vendor MICREL 0x058d Micrel vendor ALCOR 0x058f Alcor Micro vendor OMRON 0x0590 OMRON vendor ZORAN 0x0595 Zoran Microelectronics vendor NIIGATA 0x0598 Niigata vendor IOMEGA 0x059b Iomega vendor ATREND 0x059c A-Trend Technology vendor AID 0x059d Advanced Input Devices vendor LACIE 0x059f LaCie vendor FUJIFILM 0x05a2 Fuji Film vendor ARC 0x05a3 ARC vendor ORTEK 0x05a4 Ortek vendor CISCOLINKSYS3 0x05a6 Cisco-Linksys vendor BOSE 0x05a7 Bose vendor OMNIVISION 0x05a9 OmniVision vendor INSYSTEM 0x05ab In-System Design vendor APPLE 0x05ac Apple Computer vendor YCCABLE 0x05ad Y.C. Cable vendor DIGITALPERSONA 0x05ba DigitalPersona vendor 3G 0x05bc 3G Green Green Globe vendor RAFI 0x05bd RAFI vendor TYCO 0x05be Tyco vendor KAWASAKI 0x05c1 Kawasaki vendor DIGI 0x05c5 Digi International vendor QUALCOMM2 0x05c6 Qualcomm vendor QTRONIX 0x05c7 Qtronix vendor FOXLINK 0x05c8 Foxlink vendor RICOH 0x05ca Ricoh vendor ELSA 0x05cc ELSA vendor SCIWORX 0x05ce sci-worx vendor BRAINBOXES 0x05d1 Brainboxes Limited vendor ULTIMA 0x05d8 Ultima vendor AXIOHM 0x05d9 Axiohm Transaction Solutions vendor MICROTEK 0x05da Microtek vendor SUNTAC 0x05db SUN Corporation vendor LEXAR 0x05dc Lexar Media vendor ADDTRON 0x05dd Addtron vendor SYMBOL 0x05e0 Symbol Technologies vendor SYNTEK 0x05e1 Syntek vendor GENESYS 0x05e3 Genesys Logic vendor FUJI 0x05e5 Fuji Electric vendor KEITHLEY 0x05e6 Keithley Instruments vendor EIZONANAO 0x05e7 EIZO Nanao vendor KLSI 0x05e9 Kawasaki LSI vendor FFC 0x05eb FFC vendor ANKO 0x05ef Anko Electronic vendor PIENGINEERING 0x05f3 P.I. Engineering vendor AOC 0x05f6 AOC International vendor CHIC 0x05fe Chic Technology vendor BARCO 0x0600 Barco Display Systems vendor BRIDGE 0x0607 Bridge Information vendor SMK 0x0609 SMK vendor SOLIDYEAR 0x060b Solid Year vendor BIORAD 0x0614 Bio-Rad Laboratories vendor MACALLY 0x0618 Macally vendor ACTLABS 0x061c Act Labs vendor ALARIS 0x0620 Alaris vendor APEX 0x0624 Apex vendor CREATIVE3 0x062a Creative Labs vendor MICRON 0x0634 Micron Technology vendor VIVITAR 0x0636 Vivitar vendor GUNZE 0x0637 Gunze Electronics USA vendor AVISION 0x0638 Avision vendor TEAC 0x0644 TEAC vendor ACTON 0x0647 Acton Research Corp. vendor OPTO 0x065a Optoelectronics Co., Ltd vendor SGI 0x065e Silicon Graphics vendor SANWASUPPLY 0x0663 Sanwa Supply vendor MEGATEC 0x0665 Megatec vendor LINKSYS 0x066b Linksys vendor ACERSA 0x066e Acer Semiconductor America vendor SIGMATEL 0x066f Sigmatel vendor DRAYTEK 0x0675 DrayTek vendor AIWA 0x0677 Aiwa vendor ACARD 0x0678 ACARD Technology vendor PROLIFIC 0x067b Prolific Technology vendor SIEMENS 0x067c Siemens vendor AVANCELOGIC 0x0680 Avance Logic vendor SIEMENS2 0x0681 Siemens vendor MINOLTA 0x0686 Minolta vendor CHPRODUCTS 0x068e CH Products vendor HAGIWARA 0x0693 Hagiwara Sys-Com vendor CTX 0x0698 Chuntex vendor ASKEY 0x069a Askey Computer vendor SAITEK 0x06a3 Saitek vendor ALCATELT 0x06b9 Alcatel Telecom vendor AGFA 0x06bd AGFA-Gevaert vendor ASIAMD 0x06be Asia Microelectronic Development vendor BIZLINK 0x06c4 Bizlink International vendor KEYSPAN 0x06cd Keyspan / InnoSys Inc. vendor CONTEC 0x06ce Contec products vendor AASHIMA 0x06d6 Aashima Technology vendor LIEBERT 0x06da Liebert vendor MULTITECH 0x06e0 MultiTech vendor ADS 0x06e1 ADS Technologies vendor ALCATELM 0x06e4 Alcatel Microelectronics vendor SIRIUS 0x06ea Sirius Technologies vendor GUILLEMOT 0x06f8 Guillemot vendor BOSTON 0x06fd Boston Acoustics vendor SMC 0x0707 Standard Microsystems vendor PUTERCOM 0x0708 Putercom vendor MCT 0x0711 MCT vendor IMATION 0x0718 Imation vendor TECLAST 0x071b Teclast vendor SONYERICSSON 0x0731 Sony Ericsson vendor EICON 0x0734 Eicon Networks vendor MADCATZ 0x0738 Mad Catz, Inc. vendor SYNTECH 0x0745 Syntech Information vendor DIGITALSTREAM 0x074e Digital Stream vendor AUREAL 0x0755 Aureal Semiconductor vendor MAUDIO 0x0763 M-Audio vendor CYBERPOWER 0x0764 Cyber Power Systems, Inc. vendor SURECOM 0x0769 Surecom Technology vendor HIDGLOBAL 0x076b HID Global vendor LINKSYS2 0x077b Linksys vendor GRIFFIN 0x077d Griffin Technology vendor SANDISK 0x0781 SanDisk vendor JENOPTIK 0x0784 Jenoptik vendor LOGITEC 0x0789 Logitec vendor NOKIA2 0x078b Nokia vendor BRIMAX 0x078e Brimax vendor AXIS 0x0792 Axis Communications vendor ABL 0x0794 ABL Electronics vendor SAGEM 0x079b Sagem vendor SUNCOMM 0x079c Sun Communications, Inc. vendor ALFADATA 0x079d Alfadata Computer vendor NATIONALTECH 0x07a2 National Technical Systems vendor ONNTO 0x07a3 Onnto vendor BE 0x07a4 Be vendor ADMTEK 0x07a6 ADMtek vendor COREGA 0x07aa Corega vendor FREECOM 0x07ab Freecom vendor MICROTECH 0x07af Microtech vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola) vendor OLYMPUS 0x07b4 Olympus vendor ABOCOM 0x07b8 AboCom Systems vendor KINGSUN 0x07c0 KingSun vendor KEISOKUGIKEN 0x07c1 Keisokugiken vendor ONSPEC 0x07c4 OnSpec vendor APG 0x07c5 APG Cash Drawer vendor BUG 0x07c8 B.U.G. vendor ALLIEDTELESYN 0x07c9 Allied Telesyn International vendor AVERMEDIA 0x07ca AVerMedia Technologies vendor SIIG 0x07cc SIIG vendor CASIO 0x07cf CASIO vendor DLINK2 0x07d1 D-Link vendor APTIO 0x07d2 Aptio Products vendor ARASAN 0x07da Arasan Chip Systems vendor ALLIEDCABLE 0x07e6 Allied Cable vendor STSN 0x07ef STSN vendor BEWAN 0x07fa Bewan vendor CENTURY 0x07f7 Century Corp vendor NEWLINK 0x07ff NEWlink vendor MAGTEK 0x0801 Mag-Tek vendor ZOOM 0x0803 Zoom Telephonics vendor PCS 0x0810 Personal Communication Systems vendor SYNET 0x0812 Synet Electronics vendor ALPHASMART 0x081e AlphaSmart, Inc. vendor BROADLOGIC 0x0827 BroadLogic vendor HANDSPRING 0x082d Handspring vendor PALM 0x0830 Palm Computing vendor SOURCENEXT 0x0833 SOURCENEXT vendor ACTIONSTAR 0x0835 Action Star Enterprise vendor SAMSUNG_TECHWIN 0x0839 Samsung Techwin vendor ACCTON 0x083a Accton Technology vendor DIAMOND 0x0841 Diamond vendor NETGEAR 0x0846 BayNETGEAR vendor TOPRE 0x0853 Topre Corporation vendor ACTIVEWIRE 0x0854 ActiveWire vendor BBELECTRONICS 0x0856 B&B Electronics vendor PORTGEAR 0x085a PortGear vendor NETGEAR2 0x0864 Netgear vendor SYSTEMTALKS 0x086e System Talks vendor METRICOM 0x0870 Metricom vendor ADESSOKBTEK 0x087c ADESSO/Kbtek America vendor JATON 0x087d Jaton vendor APT 0x0880 APT Technologies vendor BOCARESEARCH 0x0885 Boca Research vendor ANDREA 0x08a8 Andrea Electronics vendor BURRBROWN 0x08bb Burr-Brown Japan vendor 2WIRE 0x08c8 2Wire vendor AIPTEK 0x08ca AIPTEK International vendor SMARTBRIDGES 0x08d1 SmartBridges vendor FUJITSUSIEMENS 0x08d4 Fujitsu-Siemens vendor BILLIONTON 0x08dd Billionton Systems vendor GEMALTO 0x08e6 Gemalto SA vendor EXTENDED 0x08e9 Extended Systems vendor MSYSTEMS 0x08ec M-Systems vendor DIGIANSWER 0x08fd Digianswer vendor AUTHENTEC 0x08ff AuthenTec vendor AUDIOTECHNICA 0x0909 Audio-Technica vendor TRUMPION 0x090a Trumpion Microelectronics vendor FEIYA 0x090c Feiya vendor ALATION 0x0910 Alation Systems vendor GLOBESPAN 0x0915 Globespan vendor CONCORDCAMERA 0x0919 Concord Camera vendor GARMIN 0x091e Garmin International vendor GOHUBS 0x0921 GoHubs vendor DYMO 0x0922 DYMO vendor XEROX 0x0924 Xerox vendor BIOMETRIC 0x0929 American Biometric Company vendor TOSHIBA 0x0930 Toshiba vendor PLEXTOR 0x093b Plextor vendor INTREPIDCS 0x093c Intrepid vendor YANO 0x094f Yano vendor KINGSTON 0x0951 Kingston Technology vendor NVIDIA 0x0955 NVIDIA Corporation vendor BLUEWATER 0x0956 BlueWater Systems vendor AGILENT 0x0957 Agilent Technologies vendor GUDE 0x0959 Gude ADS vendor PORTSMITH 0x095a Portsmith vendor ACERW 0x0967 Acer vendor ADIRONDACK 0x0976 Adirondack Wire & Cable vendor BECKHOFF 0x0978 Beckhoff vendor MINDSATWORK 0x097a Minds At Work vendor ZIPPY 0x099a Zippy Technology Corporation vendor POINTCHIPS 0x09a6 PointChips vendor INTERSIL 0x09aa Intersil vendor TRIPPLITE2 0x09ae Tripp Lite vendor ALTIUS 0x09b3 Altius Solutions vendor ARRIS 0x09c1 Arris Interactive vendor ACTIVCARD 0x09c3 ACTIVCARD vendor ACTISYS 0x09c4 ACTiSYS vendor NOVATEL2 0x09d7 Novatel Wireless vendor AFOURTECH 0x09da A-FOUR TECH vendor AIMEX 0x09dc AIMEX vendor ADDONICS 0x09df Addonics Technologies vendor AKAI 0x09e8 AKAI professional M.I. vendor ARESCOM 0x09f5 ARESCOM vendor BAY 0x09f9 Bay Associates vendor ALTERA 0x09fb Altera vendor CSR 0x0a12 Cambridge Silicon Radio vendor TREK 0x0a16 Trek Technology vendor ASAHIOPTICAL 0x0a17 Asahi Optical vendor BOCASYSTEMS 0x0a43 Boca Systems vendor SHANTOU 0x0a46 ShanTou vendor MEDIAGEAR 0x0a48 MediaGear vendor BROADCOM 0x0a5c Broadcom vendor GREENHOUSE 0x0a6b GREENHOUSE vendor MEDELI 0x0a67 Medeli vendor GEOCAST 0x0a79 Geocast Network Systems vendor EGO 0x0a92 EGO systems vendor IDQUANTIQUE 0x0aba ID Quantique vendor IDTECH 0x0acd ID TECH vendor ZYDAS 0x0ace Zydas Technology Corporation vendor NEODIO 0x0aec Neodio vendor OPTION 0x0af0 Option N.V. vendor ASUS 0x0b05 ASUSTeK Computer vendor TODOS 0x0b0c Todos Data System vendor SIIG2 0x0b39 SIIG vendor TEKRAM 0x0b3b Tekram Technology vendor HAL 0x0b41 HAL Corporation vendor EMS 0x0b43 EMS Production vendor NEC2 0x0b62 NEC vendor ADLINK 0x0b63 ADLINK Technoligy, Inc. vendor ATI2 0x0b6f ATI Technologies vendor ZEEVO 0x0b7a Zeevo, Inc. vendor KURUSUGAWA 0x0b7e Kurusugawa Electronics, Inc. vendor SMART 0x0b8c Smart Technologies vendor ASIX 0x0b95 ASIX Electronics vendor O2MICRO 0x0b97 O2 Micro, Inc. vendor USR 0x0baf U.S. Robotics vendor AMBIT 0x0bb2 Ambit Microsystems vendor HTC 0x0bb4 HTC vendor REALTEK 0x0bda Realtek vendor ERICSSON2 0x0bdb Ericsson vendor MEI 0x0bed MEI vendor ADDONICS2 0x0bf6 Addonics Technology vendor FSC 0x0bf8 Fujitsu Siemens Computers vendor AGATE 0x0c08 Agate Technologies vendor DMI 0x0c0b DMI vendor CANYON 0x0c10 Canyon vendor ICOM 0x0c26 Icom Inc. vendor GNOTOMETRICS 0x0c33 GN Otometrics vendor CHICONY2 0x0c45 Chicony / Microdia / Sonix Technology Co., Ltd. vendor REINERSCT 0x0c4b Reiner-SCT vendor SEALEVEL 0x0c52 Sealevel System vendor JETI 0x0c6c Jeti vendor LUWEN 0x0c76 Luwen vendor ELEKTOR 0x0c7d ELEKTOR Electronics vendor KYOCERA2 0x0c88 Kyocera Wireless Corp. vendor ZCOM 0x0cde Z-Com vendor ATHEROS2 0x0cf3 Atheros Communications vendor POSIFLEX 0x0d3a POSIFLEX vendor TANGTOP 0x0d3d Tangtop vendor KOBIL 0x0d46 KOBIL vendor SMC3 0x0d5c Standard Microsystems vendor ADDON 0x0d7d Add-on Technology vendor ACDC 0x0d7e American Computer & Digital Components vendor CMEDIA 0x0d8c CMEDIA vendor CONCEPTRONIC 0x0d8e Conceptronic vendor SKANHEX 0x0d96 Skanhex Technology, Inc. vendor MSI 0x0db0 Micro Star International vendor ELCON 0x0db7 ELCON Systemtechnik vendor UNKNOWN4 0x0dcd Unknown vendor vendor NETAC 0x0dd8 Netac vendor SITECOMEU 0x0df6 Sitecom Europe vendor MOBILEACTION 0x0df7 Mobile Action vendor AMIGO 0x0e0b Amigo Technology vendor SPEEDDRAGON 0x0e55 Speed Dragon Multimedia vendor HAWKING 0x0e66 Hawking vendor FOSSIL 0x0e67 Fossil, Inc vendor GMATE 0x0e7e G.Mate, Inc vendor MEDIATEK 0x0e8d MediaTek, Inc. vendor OTI 0x0ea0 Ours Technology vendor YISO 0x0eab Yiso Wireless Co. vendor PILOTECH 0x0eaf Pilotech vendor NOVATECH 0x0eb0 NovaTech vendor ITEGNO 0x0eba iTegno vendor WINMAXGROUP 0x0ed1 WinMaxGroup vendor TOD 0x0ede TOD vendor EGALAX 0x0eef eGalax, Inc. vendor AIRPRIME 0x0f3d AirPrime, Inc. vendor MICROTUNE 0x0f4d Microtune vendor VTECH 0x0f88 VTech vendor FALCOM 0x0f94 Falcom Wireless Communications GmbH vendor RIM 0x0fca Research In Motion vendor DYNASTREAM 0x0fcf Dynastream Innovations vendor LARSENBRUSGAARD 0x0fd8 Larsen and Brusgaard vendor OWL 0x0fde OWL vendor KONTRON 0x0fe6 Kontron AG vendor DVICO 0x0fe9 DViCO vendor QUALCOMM 0x1004 Qualcomm vendor APACER 0x1005 Apacer vendor MOTOROLA4 0x100d Motorola vendor HP3 0x103c Hewlett Packard vendor AIRPLUS 0x1011 Airplus vendor DESKNOTE 0x1019 Desknote vendor AMD2 0x1022 Advanced Micro Devices vendor NEC3 0x1033 NEC vendor TTI 0x103e Thurlby Thandar Instruments vendor GIGABYTE 0x1044 GIGABYTE vendor WESTERN 0x1058 Western Digital vendor MOTOROLA 0x1063 Motorola vendor CCYU 0x1065 CCYU Technology vendor CURITEL 0x106c Curitel Communications Inc vendor SILABS2 0x10a6 SILABS2 vendor USI 0x10ab USI vendor HONEYWELL 0x10ac Honeywell vendor LIEBERT2 0x10af Liebert vendor PLX 0x10b5 PLX vendor ASANTE 0x10bd Asante vendor SILABS 0x10c4 Silicon Labs vendor SILABS3 0x10c5 Silicon Labs vendor SILABS4 0x10ce Silicon Labs vendor ACTIONS 0x10d6 Actions vendor MOXA 0x110a Moxa vendor ANALOG 0x1110 Analog Devices vendor TENX 0x1130 Ten X Technology, Inc. vendor ISSC 0x1131 Integrated System Solution Corp. vendor JRC 0x1145 Japan Radio Company vendor SPHAIRON 0x114b Sphairon Access Systems GmbH vendor DELORME 0x1163 DeLorme vendor SERVERWORKS 0x1166 ServerWorks vendor DLINK3 0x1186 Dlink vendor ACERCM 0x1189 Acer Communications & Multimedia vendor SIERRA 0x1199 Sierra Wireless vendor SANWA 0x11ad Sanwa Electric Instrument Co., Ltd. vendor TOPFIELD 0x11db Topfield Co., Ltd vendor SIEMENS3 0x11f5 Siemens vendor NETINDEX 0x11f6 NetIndex vendor ALCATEL 0x11f7 Alcatel vendor INTERBIOMETRICS 0x1209 Interbiometrics vendor FUJITSU3 0x1221 Fujitsu Ltd. vendor UNKNOWN3 0x1233 Unknown vendor vendor TSUNAMI 0x1241 Tsunami vendor PHEENET 0x124a Pheenet vendor TARGUS 0x1267 Targus vendor TWINMOS 0x126f TwinMOS vendor TENDA 0x1286 Tenda vendor TESTO 0x128d Testo products vendor CREATIVE2 0x1292 Creative Labs vendor BELKIN2 0x1293 Belkin Components vendor CYBERTAN 0x129b CyberTAN Technology vendor HUAWEI 0x12d1 Huawei Technologies vendor ARANEUS 0x12d8 Araneus Information Systems vendor TAPWAVE 0x12ef Tapwave vendor AINCOMM 0x12fd Aincomm vendor MOBILITY 0x1342 Mobility vendor DICKSMITH 0x1371 Dick Smith Electronics vendor NETGEAR3 0x1385 Netgear vendor VALIDITY 0x138a Validity Sensors, Inc. vendor BALTECH 0x13ad Baltech vendor CISCOLINKSYS 0x13b1 Cisco-Linksys vendor SHARK 0x13d2 Shark vendor AZUREWAVE 0x13d3 AsureWave vendor INITIO 0x13fd Initio Corporation vendor EMTEC 0x13fe Emtec vendor NOVATEL 0x1410 Novatel Wireless vendor OMNIVISION2 0x1415 OmniVision Technologies, Inc. vendor MERLIN 0x1416 Merlin vendor REDOCTANE 0x1430 RedOctane vendor WISTRONNEWEB 0x1435 Wistron NeWeb vendor RADIOSHACK 0x1453 Radio Shack vendor FIC 0x1457 FIC / OpenMoko vendor HUAWEI3COM 0x1472 Huawei-3Com vendor ABOCOM2 0x1482 AboCom Systems vendor SILICOM 0x1485 Silicom vendor RALINK 0x148f Ralink Technology vendor IMAGINATION 0x149a Imagination Technologies vendor ATP 0x14af ATP Electronics vendor CONCEPTRONIC2 0x14b2 Conceptronic vendor SUPERTOP 0x14cd Super Top vendor PLANEX3 0x14ea Planex Communications vendor SILICONPORTALS 0x1527 Silicon Portals vendor UBIQUAM 0x1529 UBIQUAM Co., Ltd. vendor JMICRON 0x152d JMicron vendor UBLOX 0x1546 U-blox vendor PNY 0x154b PNY vendor OWEN 0x1555 Owen vendor OQO 0x1557 OQO vendor UMEDIA 0x157e U-MEDIA Communications vendor FIBERLINE 0x1582 Fiberline vendor FREESCALE 0x15a2 Freescale Semiconductor, Inc. vendor AFATECH 0x15a4 Afatech Technologies, Inc. vendor SPARKLAN 0x15a9 SparkLAN vendor OLIMEX 0x15ba Olimex vendor SOUNDGRAPH 0x15c2 Soundgraph, Inc. vendor AMIT2 0x15c5 AMIT vendor TEXTECH 0x15ca Textech International Ltd. vendor SOHOWARE 0x15e8 SOHOware vendor ABIT 0x15eb ABIT Corporation vendor UMAX 0x1606 UMAX Data Systems vendor INSIDEOUT 0x1608 Inside Out Networks vendor AMOI 0x1614 Amoi Electronics vendor GOODWAY 0x1631 Good Way Technology vendor ENTREGA 0x1645 Entrega vendor ACTIONTEC 0x1668 Actiontec Electronics vendor CLIPSAL 0x166a Clipsal vendor CISCOLINKSYS2 0x167b Cisco-Linksys vendor ATHEROS 0x168c Atheros Communications vendor GIGASET 0x1690 Gigaset vendor GLOBALSUN 0x16ab Global Sun Technology vendor ANYDATA 0x16d5 AnyDATA Corporation vendor JABLOTRON 0x16d6 Jablotron vendor CMOTECH 0x16d8 C-motech vendor WIENERPLEINBAUS 0x16dc WIENER Plein & Baus GmbH. vendor AXESSTEL 0x1726 Axesstel Co., Ltd. vendor LINKSYS4 0x1737 Linksys vendor SENAO 0x1740 Senao vendor ASUS2 0x1761 ASUS vendor SWEEX2 0x177f Sweex vendor METAGEEK 0x1781 MetaGeek vendor KAMSTRUP 0x17a8 Kamstrup A/S vendor MISC 0x1781 Misc Vendors vendor DISPLAYLINK 0x17e9 DisplayLink vendor LENOVO 0x17ef Lenovo vendor WAVESENSE 0x17f4 WaveSense vendor VAISALA 0x1843 Vaisala vendor E3C 0x18b4 E3C Technologies vendor AMIT 0x18c5 AMIT vendor GOOGLE 0x18d1 Google vendor QCOM 0x18e8 Qcom vendor ELV 0x18ef ELV vendor LINKSYS3 0x1915 Linksys vendor MEINBERG 0x1938 Meinberg Funkuhren vendor BECEEM 0x198f Beceem Communications vendor ZTE 0x19d2 ZTE vendor QUALCOMMINC 0x19d2 Qualcomm, Incorporated vendor QUALCOMM3 0x19f5 Qualcomm, Inc. vendor QUANTA2 0x1a32 Quanta vendor TERMINUS 0x1a40 Terminus Technology vendor ABBOTT 0x1a61 Abbott Diabetics vendor BAYER 0x1a79 Bayer vendor WCH2 0x1a86 QinHeng Electronics vendor STELERA 0x1a8d Stelera Wireless vendor SEL 0x1adb Schweitzer Engineering Laboratories vendor CORSAIR 0x1b1c Corsair vendor ASM 0x1b21 ASMedia Technology vendor MATRIXORBITAL 0x1b3d Matrix Orbital vendor OVISLINK 0x1b75 OvisLink vendor TML 0x1b91 The Mobility Lab vendor TCTMOBILE 0x1bbb TCT Mobile vendor ALTI2 0x1bc9 Alti-2 products vendor SUNPLUS 0x1bcf Sunplus Innovation Technology Inc. vendor WAGO 0x1be3 WAGO Kontakttechnik GmbH. vendor TELIT 0x1bc7 Telit vendor IONICS 0x1c0c Ionics PlugComputer vendor LONGCHEER 0x1c9e Longcheer Holdings, Ltd. vendor MPMAN 0x1cae MpMan vendor DRESDENELEKTRONIK 0x1cf1 dresden elektronik vendor NEOTEL 0x1d09 Neotel vendor DREAMLINK 0x1d34 Dream Link vendor PEGATRON 0x1d4d Pegatron vendor QISDA 0x1da5 Qisda vendor METAGEEK2 0x1dd5 MetaGeek vendor ALINK 0x1e0e Alink vendor AIRTIES 0x1eda AirTies vendor FESTO 0x1e29 Festo vendor LAKESHORE 0x1fb9 Lake Shore Cryotronics, Inc. vendor VERTEX 0x1fe7 Vertex Wireless Co., Ltd. vendor DLINK 0x2001 D-Link vendor PLANEX2 0x2019 Planex Communications vendor HAUPPAUGE2 0x2040 Hauppauge Computer Works vendor TLAYTECH 0x20b9 Tlay Tech vendor ENCORE 0x203d Encore vendor QIHARDWARE 0x20b7 QI-hardware vendor PARA 0x20b8 PARA Industrial vendor SIMTEC 0x20df Simtec Electronics vendor TRENDNET 0x20f4 TRENDnet vendor RTSYSTEMS 0x2100 RT Systems vendor DLINK4 0x2101 D-Link vendor INTENSO 0x2109 INTENSO vendor VIALABS 0x2109 VIA Labs vendor ERICSSON 0x2282 Ericsson vendor MOTOROLA2 0x22b8 Motorola vendor WETELECOM 0x22de WeTelecom vendor PINNACLE 0x2304 Pinnacle Systems vendor ARDUINO 0x2341 Arduino SA vendor TPLINK 0x2357 TP-Link vendor WESTMOUNTAIN 0x2405 West Mountain Radio vendor TRIPPLITE 0x2478 Tripp-Lite vendor HIROSE 0x2631 Hirose Electric vendor NHJ 0x2770 NHJ vendor THINGM 0x27b8 ThingM vendor PERASO 0x2932 Peraso Technologies, Inc. vendor PLANEX 0x2c02 Planex Communications vendor QUECTEL 0x2c7c Quectel Wireless Solutions vendor VIDZMEDIA 0x3275 VidzMedia Pte Ltd vendor LINKINSTRUMENTS 0x3195 Link Instruments Inc. vendor AEI 0x3334 AEI vendor HANK 0x3353 Hank Connection vendor PQI 0x3538 PQI vendor DAISY 0x3579 Daisy Technology vendor NI 0x3923 National Instruments vendor MICRONET 0x3980 Micronet Communications vendor IODATA2 0x40bb I-O Data vendor IRIVER 0x4102 iRiver vendor DELL 0x413c Dell vendor WCH 0x4348 QinHeng Electronics vendor ACEECA 0x4766 Aceeca vendor FEIXUN 0x4855 FeiXun Communication vendor PAPOUCH 0x5050 Papouch products vendor AVERATEC 0x50c2 Averatec vendor SWEEX 0x5173 Sweex vendor PROLIFIC2 0x5372 Prolific Technologies vendor ONSPEC2 0x55aa OnSpec Electronic Inc. vendor ZINWELL 0x5a57 Zinwell vendor INGENIC 0x601a Ingenic Semiconductor Ltd. vendor SITECOM 0x6189 Sitecom vendor SPRINGERDESIGN 0x6400 Springer Design, Inc. vendor ARKMICRO 0x6547 Arkmicro Technologies Inc. vendor 3COM2 0x6891 3Com vendor EDIMAX 0x7392 Edimax vendor INTEL 0x8086 Intel vendor INTEL2 0x8087 Intel vendor ALLWIN 0x8516 ALLWIN Tech vendor SITECOM2 0x9016 Sitecom vendor MOSCHIP 0x9710 MosChip Semiconductor vendor NETGEAR4 0x9846 Netgear vendor MARVELL 0x9e88 Marvell Technology Group Ltd. vendor 3COM3 0xa727 3Com vendor CACE 0xcace CACE Technologies vendor COMPARE 0xcdab Compare vendor DATAAPEX 0xdaae DataApex vendor EVOLUTION 0xdeee Evolution Robotics vendor EMPIA 0xeb1a eMPIA Technology vendor HP2 0xf003 Hewlett Packard vendor LOGILINK 0xfc08 LogiLink vendor USRP 0xfffe GNU Radio USRP /* * List of known products. Grouped by vendor. */ /* 3Com products */ product 3COM HOMECONN 0x009d HomeConnect USB Camera product 3COM 3CREB96 0x00a0 Bluetooth USB Adapter product 3COM 3C19250 0x03e8 3C19250 Ethernet Adapter product 3COM 3CRSHEW696 0x0a01 3CRSHEW696 Wireless Adapter product 3COM 3C460 0x11f8 HomeConnect 3C460 product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice FaxModem Pro product 3COM 3C460B 0x4601 HomeConnect 3C460B product 3COM2 3CRUSB10075 0xa727 3CRUSB10075 product 3COM3 AR5523_1 0x6893 AR5523 product 3COM3 AR5523_2 0x6895 AR5523 product 3COM3 AR5523_3 0x6897 AR5523 product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA product 3COMUSR HOMECONN 0x009d 3Com HomeConnect Camera product 3COMUSR USR56K 0x3021 U.S. Robotics 56000 Voice FaxModem Pro /* Abbott Diabetics */ product ABBOTT STEREO_PLUG 0x3410 Abbott Diabetics Stereo Plug product ABBOTT STRIP_PORT 0x3420 Abbott Diabetics Strip Port /* ABIT products */ product ABIT AK_020 0x7d0e 3G modem product ACDC HUB 0x2315 USB Pen Drive HUB product ACDC SECWRITE 0x2316 USB Pen Drive Secure Write product ACDC PEN 0x2317 USB Pen Drive with Secure Write /* AboCom products */ product ABOCOM XX1 0x110c XX1 product ABOCOM XX2 0x200c XX2 product ABOCOM RT2770 0x2770 RT2770 product ABOCOM RT2870 0x2870 RT2870 product ABOCOM RT3070 0x3070 RT3070 product ABOCOM RT3071 0x3071 RT3071 product ABOCOM RT3072 0x3072 RT3072 product ABOCOM2 RT2870_1 0x3c09 RT2870 product ABOCOM URE450 0x4000 URE450 Ethernet Adapter product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet Adapter product ABOCOM DSB650TX_PNA 0x4003 1/10/100 Ethernet Adapter product ABOCOM XX4 0x4004 XX4 product ABOCOM XX5 0x4007 XX5 product ABOCOM XX6 0x400b XX6 product ABOCOM XX7 0x400c XX7 product ABOCOM RTL8151 0x401a RTL8151 product ABOCOM XX8 0x4102 XX8 product ABOCOM XX9 0x4104 XX9 product ABOCOM UF200 0x420a UF200 Ethernet product ABOCOM WL54 0x6001 WL54 product ABOCOM XX10 0xabc1 XX10 product ABOCOM BWU613 0xb000 BWU613 product ABOCOM HWU54DM 0xb21b HWU54DM product ABOCOM RT2573_2 0xb21c RT2573 product ABOCOM RT2573_3 0xb21d RT2573 product ABOCOM RT2573_4 0xb21e RT2573 product ABOCOM RTL8188CU_1 0x8188 RTL8188CU product ABOCOM RTL8188CU_2 0x8189 RTL8188CU product ABOCOM RTL8192CU 0x8178 RTL8192CU product ABOCOM RTL8188EU 0x8179 RTL8188EU product ABOCOM WUG2700 0xb21f WUG2700 /* Acton Research Corp. */ product ACTON SPECTRAPRO 0x0100 FTDI compatible adapter /* Accton products */ product ACCTON USB320_EC 0x1046 USB320-EC Ethernet Adapter product ACCTON 2664W 0x3501 2664W product ACCTON 111 0x3503 T-Sinus 111 Wireless Adapter product ACCTON SMCWUSBG_NF 0x4505 SMCWUSB-G (no firmware) product ACCTON SMCWUSBG 0x4506 SMCWUSB-G product ACCTON SMCWUSBTG2_NF 0x4507 SMCWUSBT-G2 (no firmware) product ACCTON SMCWUSBTG2 0x4508 SMCWUSBT-G2 product ACCTON PRISM_GT 0x4521 PrismGT USB 2.0 WLAN product ACCTON SS1001 0x5046 SpeedStream Ethernet Adapter product ACCTON RT2870_2 0x6618 RT2870 product ACCTON RT3070 0x7511 RT3070 product ACCTON RT2770 0x7512 RT2770 product ACCTON RT2870_3 0x7522 RT2870 product ACCTON RT2870_5 0x8522 RT2870 product ACCTON RT3070_4 0xa512 RT3070 product ACCTON RT2870_4 0xa618 RT2870 product ACCTON RT3070_1 0xa701 RT3070 product ACCTON RT3070_2 0xa702 RT3070 product ACCTON RT2870_1 0xb522 RT2870 product ACCTON RT3070_3 0xc522 RT3070 product ACCTON RT3070_5 0xd522 RT3070 product ACCTON RTL8192SU 0xc512 RTL8192SU product ACCTON ZD1211B 0xe501 ZD1211B product ACCTON WN7512 0xf522 WN7512 /* Aceeca products */ product ACEECA MEZ1000 0x0001 MEZ1000 RDA /* Acer Communications & Multimedia (oemd by Surecom) */ product ACERCM EP1427X2 0x0893 EP-1427X-2 Ethernet Adapter /* Acer Labs products */ product ACERLABS M5632 0x5632 USB 2.0 Data Link /* Acer Peripherals, Inc. products */ product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U product ACERP ACERSCAN_320U 0x2022 Acerscan 320U product ACERP ACERSCAN_640U 0x2040 Acerscan 640U product ACERP ACERSCAN_620U 0x2060 Acerscan 620U product ACERP ACERSCAN_4300U 0x20b0 Benq 3300U/4300U product ACERP ACERSCAN_640BT 0x20be Acerscan 640BT product ACERP ACERSCAN_1240U 0x20c0 Acerscan 1240U product ACERP S81 0x4027 BenQ S81 phone product ACERP H10 0x4068 AWL400 Wireless Adapter product ACERP ATAPI 0x6003 ATA/ATAPI Adapter product ACERP AWL300 0x9000 AWL300 Wireless Adapter product ACERP AWL400 0x9001 AWL400 Wireless Adapter /* Acer Warp products */ product ACERW WARPLINK 0x0204 Warplink /* Actions products */ product ACTIONS MP4 0x1101 Actions MP4 Player /* Actiontec, Inc. products */ product ACTIONTEC PRISM_25 0x0408 Prism2.5 Wireless Adapter product ACTIONTEC PRISM_25A 0x0421 Prism2.5 Wireless Adapter A product ACTIONTEC FREELAN 0x6106 ROPEX FreeLan 802.11b product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet Adapter /* ACTiSYS products */ product ACTISYS IR2000U 0x0011 ACT-IR2000U FIR /* ActiveWire, Inc. products */ product ACTIVEWIRE IOBOARD 0x0100 I/O Board product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware /* Adaptec products */ product ADAPTEC AWN8020 0x0020 AWN-8020 WLAN /* Addonics products */ product ADDONICS2 205 0xa001 Cable 205 /* Addtron products */ product ADDTRON AWU120 0xff31 AWU-120 /* ADLINK Texhnology products */ product ADLINK ND6530 0x6530 ND-6530 USB-Serial /* ADMtek products */ product ADMTEK PEGASUSII_4 0x07c2 AN986A Ethernet product ADMTEK PEGASUS 0x0986 AN986 Ethernet product ADMTEK PEGASUSII 0x8511 AN8511 Ethernet product ADMTEK PEGASUSII_2 0x8513 AN8513 Ethernet product ADMTEK PEGASUSII_3 0x8515 AN8515 Ethernet /* ADDON products */ /* PNY OEMs these */ product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON A256MB 0x1400 Attache 256MB USB 2.0 Flash Drive product ADDON DISKPRO512 0x1420 USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive) /* Addonics products */ product ADDONICS2 CABLE_205 0xa001 Cable 205 /* ADS products */ product ADS UBS10BT 0x0008 UBS-10BT Ethernet product ADS UBS10BTX 0x0009 UBS-10BT Ethernet /* AEI products */ product AEI FASTETHERNET 0x1701 Fast Ethernet /* Afatech Technologies, Inc. */ product AFATECH AFATECH1336 0x1336 Flash Card Reader /* Agate Technologies products */ product AGATE QDRIVE 0x0378 Q-Drive /* AGFA products */ product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U product AGFA SNAPSCANE40 0x208d SnapScan e40 product AGFA SNAPSCANE50 0x208f SnapScan e50 product AGFA SNAPSCANE20 0x2091 SnapScan e20 product AGFA SNAPSCANE25 0x2095 SnapScan e25 product AGFA SNAPSCANE26 0x2097 SnapScan e26 product AGFA SNAPSCANE52 0x20fd SnapScan e52 /* Ain Communication Technology products */ product AINCOMM AWU2000B 0x1001 AWU2000B Wireless Adapter /* AIPTEK products */ product AIPTEK POCKETCAM3M 0x2011 PocketCAM 3Mega product AIPTEK2 PENCAM_MEGA_1_3 0x504a PenCam Mega 1.3 product AIPTEK2 SUNPLUS_TECH 0x0c15 Sunplus Technology Inc. /* AirPlis products */ product AIRPLUS MCD650 0x3198 MCD650 modem /* AirPrime products */ product AIRPRIME PC5220 0x0112 CDMA Wireless PC Card product AIRPRIME USB308 0x68A3 USB308 HSPA+ USB Modem product AIRPRIME AC313U 0x68aa Sierra Wireless AirCard 313U /* AirTies products */ product AIRTIES RT3070 0x2310 RT3070 /* AKS products */ product AKS USBHASP 0x0001 USB-HASP 0.06 /* Alcatel products */ product ALCATEL OT535 0x02df One Touch 535/735 /* Alcor Micro, Inc. products */ product ALCOR2 KBD_HUB 0x2802 Kbd Hub product ALCOR DUMMY 0x0000 Dummy product product ALCOR SDCR_6335 0x6335 SD/MMC Card Reader product ALCOR SDCR_6362 0x6362 SD/MMC Card Reader product ALCOR SDCR_6366 0x6366 SD/MMC Card Reader product ALCOR TRANSCEND 0x6387 Transcend JetFlash Drive product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub product ALCOR AU9814 0x9215 AU9814 Hub product ALCOR UMCR_9361 0x9361 USB Multimedia Card Reader product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub product ALCOR AU9720 0x9720 USB2 - RS-232 product ALCOR AU6390 0x6390 AU6390 USB-IDE converter /* Alink products */ product ALINK DWM652U5 0xce16 DWM-652 product ALINK 3G 0x9000 3G modem product ALINK SIM7600E 0x9001 LTE modem product ALINK 3GU 0x9200 3G modem /* Altec Lansing products */ product ALTEC ADA70 0x0070 ADA70 Speakers product ALTEC ASC495 0xff05 ASC495 Speakers /* Alti-2 products */ product ALTI2 N3 0x6001 FTDI compatible adapter /* Allied Telesyn International products */ product ALLIEDTELESYN ATUSB100 0xb100 AT-USB100 /* ALLWIN Tech products */ product ALLWIN RT2070 0x2070 RT2070 product ALLWIN RT2770 0x2770 RT2770 product ALLWIN RT2870 0x2870 RT2870 product ALLWIN RT3070 0x3070 RT3070 product ALLWIN RT3071 0x3071 RT3071 product ALLWIN RT3072 0x3072 RT3072 product ALLWIN RT3572 0x3572 RT3572 /* AlphaSmart, Inc. products */ product ALPHASMART DANA_KB 0xdbac AlphaSmart Dana Keyboard product ALPHASMART DANA_SYNC 0xdf00 AlphaSmart Dana HotSync /* Amoi products */ product AMOI H01 0x0800 H01 3G modem product AMOI H01A 0x7002 H01A 3G modem product AMOI H02 0x0802 H02 3G modem /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Ambit Microsystems products */ product AMBIT WLAN 0x0302 WLAN product AMBIT NTL_250 0x6098 NTL 250 cable modem /* Apacer products */ product APACER HT202 0xb113 USB 2.0 Flash Drive /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Amigo Technology products */ product AMIGO RT2870_1 0x9031 RT2870 product AMIGO RT2870_2 0x9041 RT2870 /* AMIT products */ product AMIT CGWLUSB2GO 0x0002 CG-WLUSB2GO product AMIT CGWLUSB2GNR 0x0008 CG-WLUSB2GNR product AMIT RT2870_1 0x0012 RT2870 /* AMIT(2) products */ product AMIT2 RT2870 0x0008 RT2870 /* Analog Devices products */ product ANALOGDEVICES GNICE 0xf000 FTDI compatible adapter product ANALOGDEVICES GNICEPLUS 0xf001 FTDI compatible adapter /* Anchor products */ product ANCHOR SERIAL 0x2008 Serial product ANCHOR EZUSB 0x2131 EZUSB product ANCHOR EZLINK 0x2720 EZLINK /* AnyData products */ product ANYDATA ADU_620UW 0x6202 CDMA 2000 EV-DO USB Modem product ANYDATA ADU_E100X 0x6501 CDMA 2000 1xRTT/EV-DO USB Modem product ANYDATA ADU_500A 0x6502 CDMA 2000 EV-DO USB Modem /* AOX, Inc. products */ product AOX USB101 0x0008 Ethernet /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Apple Computer products */ product APPLE DUMMY 0x0000 Dummy product product APPLE IMAC_KBD 0x0201 USB iMac Keyboard product APPLE KBD 0x0202 USB Keyboard M2452 product APPLE EXT_KBD 0x020c Apple Extended USB Keyboard /* MacbookAir, aka wellspring */ product APPLE WELLSPRING_ANSI 0x0223 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING_ISO 0x0224 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING_JIS 0x0225 Apple Internal Keyboard/Trackpad /* MacbookProPenryn, aka wellspring2 */ product APPLE WELLSPRING2_ANSI 0x0230 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING2_ISO 0x0231 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING2_JIS 0x0232 Apple Internal Keyboard/Trackpad /* Macbook5,1 (unibody), aka wellspring3 */ product APPLE WELLSPRING3_ANSI 0x0236 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING3_ISO 0x0237 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING3_JIS 0x0238 Apple Internal Keyboard/Trackpad /* MacbookAir3,2 (unibody), aka wellspring4 */ product APPLE WELLSPRING4_ANSI 0x023f Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4_ISO 0x0240 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4_JIS 0x0241 Apple Internal Keyboard/Trackpad /* MacbookAir3,1 (unibody), aka wellspring4 */ product APPLE WELLSPRING4A_ANSI 0x0242 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4A_ISO 0x0243 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4A_JIS 0x0244 Apple Internal Keyboard/Trackpad /* Macbook8 (unibody, March 2011) */ product APPLE WELLSPRING5_ANSI 0x0245 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5_ISO 0x0246 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5_JIS 0x0247 Apple Internal Keyboard/Trackpad /* MacbookAir4,1 (unibody, July 2011) */ product APPLE WELLSPRING6A_ANSI 0x0249 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6A_ISO 0x024a Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6A_JIS 0x024b Apple Internal Keyboard/Trackpad /* MacbookAir4,2 (unibody, July 2011) */ product APPLE WELLSPRING6_ANSI 0x024c Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6_ISO 0x024d Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6_JIS 0x024e Apple Internal Keyboard/Trackpad /* Macbook8,2 (unibody) */ product APPLE WELLSPRING5A_ANSI 0x0252 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5A_ISO 0x0253 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5A_JIS 0x0254 Apple Internal Keyboard/Trackpad /* MacbookPro10,1 (unibody, June 2012) */ product APPLE WELLSPRING7_ANSI 0x0262 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7_ISO 0x0263 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7_JIS 0x0264 Apple Internal Keyboard/Trackpad /* MacbookPro10,2 (unibody, October 2012) */ product APPLE WELLSPRING7A_ANSI 0x0259 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7A_ISO 0x025a Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7A_JIS 0x025b Apple Internal Keyboard/Trackpad /* MacbookAir6,2 (unibody, June 2013) */ product APPLE WELLSPRING8_ANSI 0x0290 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING8_ISO 0x0291 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING8_JIS 0x0292 Apple Internal Keyboard/Trackpad /* MacbookPro12,1 */ product APPLE WELLSPRING9_ANSI 0x0272 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING9_ISO 0x0273 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING9_JIS 0x0274 Apple Internal Keyboard/Trackpad product APPLE MOUSE 0x0301 Mouse M4848 product APPLE OPTMOUSE 0x0302 Optical mouse product APPLE MIGHTYMOUSE 0x0304 Mighty Mouse product APPLE KBD_HUB 0x1001 Hub in Apple USB Keyboard product APPLE EXT_KBD_HUB 0x1003 Hub in Apple Extended USB Keyboard product APPLE SPEAKERS 0x1101 Speakers product APPLE IPOD 0x1201 iPod product APPLE IPOD2G 0x1202 iPod 2G product APPLE IPOD3G 0x1203 iPod 3G product APPLE IPOD_04 0x1204 iPod '04' product APPLE IPODMINI 0x1205 iPod Mini product APPLE IPOD_06 0x1206 iPod '06' product APPLE IPOD_07 0x1207 iPod '07' product APPLE IPOD_08 0x1208 iPod '08' product APPLE IPODVIDEO 0x1209 iPod Video product APPLE IPODNANO 0x120a iPod Nano product APPLE IPHONE 0x1290 iPhone product APPLE IPOD_TOUCH 0x1291 iPod Touch product APPLE IPHONE_3G 0x1292 iPhone 3G product APPLE IPHONE_3GS 0x1294 iPhone 3GS product APPLE IPHONE_4 0x1297 iPhone 4 product APPLE IPHONE_4S 0x12a0 iPhone 4S product APPLE IPHONE_5 0x12a8 iPhone 5 product APPLE IPAD 0x129a iPad product APPLE ETHERNET 0x1402 Ethernet A1277 /* Arkmicro Technologies */ product ARKMICRO ARK3116 0x0232 ARK3116 Serial /* Asahi Optical products */ product ASAHIOPTICAL OPTIO230 0x0004 Digital camera product ASAHIOPTICAL OPTIO330 0x0006 Digital camera /* Asante products */ product ASANTE EA 0x1427 Ethernet /* ASIX Electronics products */ product ASIX AX88172 0x1720 10/100 Ethernet product ASIX AX88178 0x1780 AX88178 product ASIX AX88178A 0x178a AX88178A USB 2.0 10/100/1000 Ethernet product ASIX AX88179 0x1790 AX88179 USB 3.0 10/100/1000 Ethernet product ASIX AX88772 0x7720 AX88772 product ASIX AX88772A 0x772a AX88772A USB 2.0 10/100 Ethernet product ASIX AX88772B 0x772b AX88772B USB 2.0 10/100 Ethernet product ASIX AX88772B_1 0x7e2b AX88772B USB 2.0 10/100 Ethernet /* ASUS products */ product ASUS2 USBN11 0x0b05 USB-N11 product ASUS RT2570 0x1706 RT2500USB Wireless Adapter product ASUS WL167G 0x1707 WL-167g Wireless Adapter product ASUS WL159G 0x170c WL-159g product ASUS A9T_WIFI 0x171b A9T wireless product ASUS P5B_WIFI 0x171d P5B wireless product ASUS RT2573_1 0x1723 RT2573 product ASUS RT2573_2 0x1724 RT2573 product ASUS LCM 0x1726 LCM display product ASUS RT2870_1 0x1731 RT2870 product ASUS RT2870_2 0x1732 RT2870 product ASUS RT2870_3 0x1742 RT2870 product ASUS RT2870_4 0x1760 RT2870 product ASUS RT2870_5 0x1761 RT2870 product ASUS USBN13 0x1784 USB-N13 product ASUS USBN10 0x1786 USB-N10 product ASUS RT3070_1 0x1790 RT3070 product ASUS RTL8192SU 0x1791 RTL8192SU product ASUS USB_N53 0x179d ASUS Black Diamond Dual Band USB-N53 product ASUS RTL8192CU 0x17ab RTL8192CU product ASUS USBN66 0x17ad USB-N66 product ASUS USBN10NANO 0x17ba USB-N10 Nano product ASUS USBAC51 0x17d1 USB-AC51 product ASUS USBAC56 0x17d2 USB-AC56 product ASUS A730W 0x4202 ASUS MyPal A730W product ASUS P535 0x420f ASUS P535 PDA product ASUS GMSC 0x422f ASUS Generic Mass Storage /* ATen products */ product ATEN UC1284 0x2001 Parallel printer product ATEN UC10T 0x2002 10Mbps Ethernet product ATEN UC110T 0x2007 UC-110T Ethernet product ATEN UC232A 0x2008 Serial product ATEN UC210T 0x2009 UC-210T Ethernet product ATEN DSB650C 0x4000 DSB-650C /* ATP Electronics products */ product ATP EUSB 0xaf01 ATP IG eUSB SSD /* Atheros Communications products */ product ATHEROS AR5523 0x0001 AR5523 product ATHEROS AR5523_NF 0x0002 AR5523 (no firmware) product ATHEROS2 AR5523_1 0x0001 AR5523 product ATHEROS2 AR5523_1_NF 0x0002 AR5523 (no firmware) product ATHEROS2 AR5523_2 0x0003 AR5523 product ATHEROS2 AR5523_2_NF 0x0004 AR5523 (no firmware) product ATHEROS2 AR5523_3 0x0005 AR5523 product ATHEROS2 AR5523_3_NF 0x0006 AR5523 (no firmware) product ATHEROS2 TG121N 0x1001 TG121N product ATHEROS2 WN821NV2 0x1002 WN821NV2 product ATHEROS2 3CRUSBN275 0x1010 3CRUSBN275 product ATHEROS2 WN612 0x1011 WN612 product ATHEROS2 AR9170 0x9170 AR9170 /* Atmel Comp. products */ product ATMEL STK541 0x2109 Zigbee Controller product ATMEL UHB124 0x3301 AT43301 USB 1.1 Hub product ATMEL DWL120 0x7603 DWL-120 Wireless Adapter product ATMEL BW002 0x7605 BW002 Wireless Adapter product ATMEL WL1130USB 0x7613 WL-1130 USB product ATMEL AT76C505A 0x7614 AT76c505a Wireless Adapter /* AuthenTec products */ product AUTHENTEC AES1610 0x1600 AES1610 Fingerprint Sensor /* Avision products */ product AVISION 1200U 0x0268 1200U scanner /* AVM products */ product AVM FRITZWLAN 0x8401 FRITZ!WLAN N /* Axesstel products */ product AXESSTEL DATAMODEM 0x1000 Data Modem /* AsureWave products */ product AZUREWAVE RT2870_1 0x3247 RT2870 product AZUREWAVE RT2870_2 0x3262 RT2870 product AZUREWAVE RT3070_1 0x3273 RT3070 product AZUREWAVE RT3070_2 0x3284 RT3070 product AZUREWAVE RT3070_3 0x3305 RT3070 product AZUREWAVE RTL8188CU 0x3357 RTL8188CU product AZUREWAVE RTL8188CE_1 0x3358 RTL8188CE product AZUREWAVE RTL8188CE_2 0x3359 RTL8188CE product AZUREWAVE RTL8192SU_1 0x3306 RTL8192SU product AZUREWAVE RTL8192SU_2 0x3309 RTL8192SU product AZUREWAVE RTL8192SU_3 0x3310 RTL8192SU product AZUREWAVE RTL8192SU_4 0x3311 RTL8192SU product AZUREWAVE RTL8192SU_5 0x3325 RTL8192SU /* Baltech products */ product BALTECH CARDREADER 0x9999 Card reader /* Bayer products */ product BAYER CONTOUR_CABLE 0x6001 FTDI compatible adapter /* B&B Electronics products */ product BBELECTRONICS USOTL4 0xAC01 RS-422/485 product BBELECTRONICS 232USB9M 0xac27 FTDI compatible adapter product BBELECTRONICS 485USB9F_2W 0xac25 FTDI compatible adapter product BBELECTRONICS 485USB9F_4W 0xac26 FTDI compatible adapter product BBELECTRONICS 485USBTB_2W 0xac33 FTDI compatible adapter product BBELECTRONICS 485USBTB_4W 0xac34 FTDI compatible adapter product BBELECTRONICS TTL3USB9M 0xac50 FTDI compatible adapter product BBELECTRONICS TTL5USB9M 0xac49 FTDI compatible adapter product BBELECTRONICS USO9ML2 0xac03 FTDI compatible adapter product BBELECTRONICS USO9ML2DR 0xac17 FTDI compatible adapter product BBELECTRONICS USO9ML2DR_2 0xac16 FTDI compatible adapter product BBELECTRONICS USOPTL4 0xac11 FTDI compatible adapter product BBELECTRONICS USOPTL4DR 0xac19 FTDI compatible adapter product BBELECTRONICS USOPTL4DR2 0xac18 FTDI compatible adapter product BBELECTRONICS USPTL4 0xac12 FTDI compatible adapter product BBELECTRONICS USTL4 0xac02 FTDI compatible adapter product BBELECTRONICS ZZ_PROG1_USB 0xba02 FTDI compatible adapter /* Belkin products */ /*product BELKIN F5U111 0x???? F5U111 Ethernet*/ product BELKIN F5D6050 0x0050 F5D6050 802.11b Wireless Adapter product BELKIN FBT001V 0x0081 FBT001v2 Bluetooth product BELKIN FBT003V 0x0084 FBT003v2 Bluetooth product BELKIN F5U103 0x0103 F5U103 Serial product BELKIN F5U109 0x0109 F5U109 Serial product BELKIN USB2SCSI 0x0115 USB to SCSI product BELKIN F8T012 0x0121 F8T012xx1 Bluetooth USB Adapter product BELKIN USB2LAN 0x0121 USB to LAN product BELKIN F5U208 0x0208 F5U208 VideoBus II product BELKIN F5U237 0x0237 F5U237 USB 2.0 7-Port Hub product BELKIN F5U257 0x0257 F5U257 Serial product BELKIN F5U409 0x0409 F5U409 Serial product BELKIN F6C550AVR 0x0551 F6C550-AVR UPS product BELKIN F5U120 0x1203 F5U120-PC Hub product BELKIN RTL8188CU 0x1102 RTL8188CU Wireless Adapter product BELKIN F9L1103 0x1103 F9L1103 Wireless Adapter product BELKIN RTL8192CU 0x2102 RTL8192CU Wireless Adapter product BELKIN F7D2102 0x2103 F7D2102 Wireless Adapter product BELKIN F5U258 0x258A F5U258 Host to Host cable product BELKIN ZD1211B 0x4050 ZD1211B product BELKIN F5D5055 0x5055 F5D5055 product BELKIN F5D7050 0x7050 F5D7050 Wireless Adapter product BELKIN F5D7051 0x7051 F5D7051 54g USB Network Adapter product BELKIN F5D7050A 0x705a F5D7050A Wireless Adapter /* Also sold as 'Ativa 802.11g wireless card' */ product BELKIN F5D7050_V4000 0x705c F5D7050 v4000 Wireless Adapter product BELKIN F5D7050E 0x705e F5D7050E Wireless Adapter product BELKIN RT2870_1 0x8053 RT2870 product BELKIN RT2870_2 0x805c RT2870 product BELKIN F5D8053V3 0x815c F5D8053 v3 product BELKIN RTL8192SU_1 0x815f RTL8192SU product BELKIN RTL8192SU_2 0x845a RTL8192SU product BELKIN RTL8192SU_3 0x945a RTL8192SU product BELKIN F5D8055 0x825a F5D8055 product BELKIN F5D8055V2 0x825b F5D8055 v2 product BELKIN F5D9050V3 0x905b F5D9050 ver 3 Wireless Adapter product BELKIN2 F5U002 0x0002 F5U002 Parallel printer product BELKIN F6D4050V1 0x935a F6D4050 v1 product BELKIN F6D4050V2 0x935b F6D4050 v2 /* Billionton products */ product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet product BILLIONTON USBLP100 0x0987 USB100LP product BILLIONTON USBEL100 0x0988 USB100EL product BILLIONTON USBE100 0x8511 USBE100 product BILLIONTON USB2AR 0x90ff USB2AR Ethernet /* Broadcom products */ product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle /* Brother Industries products */ product BROTHER HL1050 0x0002 HL-1050 laser printer product BROTHER MFC8600_9650 0x0100 MFC8600/9650 multifunction device /* Behavior Technology Computer products */ product BTC BTC6100 0x5550 6100C Keyboard product BTC BTC7932 0x6782 Keyboard with mouse port /* CACE Technologies products */ product CACE AIRPCAPNX 0x0300 AirPcap NX /* Canon, Inc. products */ product CANON N656U 0x2206 CanoScan N656U product CANON N1220U 0x2207 CanoScan N1220U product CANON D660U 0x2208 CanoScan D660U product CANON N676U 0x220d CanoScan N676U product CANON N1240U 0x220e CanoScan N1240U product CANON LIDE25 0x2220 CanoScan LIDE 25 product CANON S10 0x3041 PowerShot S10 product CANON S100 0x3045 PowerShot S100 product CANON S200 0x3065 PowerShot S200 product CANON REBELXT 0x30ef Digital Rebel XT /* CATC products */ product CATC NETMATE 0x000a Netmate Ethernet product CATC NETMATE2 0x000c Netmate2 Ethernet product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer product CATC ANDROMEDA 0x1237 Andromeda hub /* CASIO products */ product CASIO QV_DIGICAM 0x1001 QV DigiCam product CASIO EXS880 0x1105 Exilim EX-S880 product CASIO BE300 0x2002 BE-300 PDA product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB /* CCYU products */ product CCYU ED1064 0x2136 EasyDisk ED1064 /* Century products */ product CENTURY EX35QUAT 0x011e Century USB Disk Enclosure product CENTURY EX35SW4_SB4 0x011f Century USB Disk Enclosure /* Cherry products */ product CHERRY MY3000KBD 0x0001 My3000 keyboard product CHERRY MY3000HUB 0x0003 My3000 hub product CHERRY CYBOARD 0x0004 CyBoard Keyboard /* Chic Technology products */ product CHIC MOUSE1 0x0001 mouse product CHIC CYPRESS 0x0003 Cypress USB Mouse /* Chicony products */ product CHICONY KB8933 0x0001 KB-8933 keyboard product CHICONY KU0325 0x0116 KU-0325 keyboard product CHICONY CNF7129 0xb071 Notebook Web Camera product CHICONY HDUVCCAM 0xb40a HD UVC WebCam product CHICONY RTL8188CUS_1 0xaff7 RTL8188CUS product CHICONY RTL8188CUS_2 0xaff8 RTL8188CUS product CHICONY RTL8188CUS_3 0xaff9 RTL8188CUS product CHICONY RTL8188CUS_4 0xaffa RTL8188CUS product CHICONY RTL8188CUS_5 0xaffa RTL8188CUS product CHICONY2 TWINKLECAM 0x600d TwinkleCam USB camera /* CH Products */ product CHPRODUCTS PROTHROTTLE 0x00f1 Pro Throttle product CHPRODUCTS PROPEDALS 0x00f2 Pro Pedals product CHPRODUCTS FIGHTERSTICK 0x00f3 Fighterstick product CHPRODUCTS FLIGHTYOKE 0x00ff Flight Sim Yoke /* Cisco-Linksys products */ product CISCOLINKSYS WUSB54AG 0x000c WUSB54AG Wireless Adapter product CISCOLINKSYS WUSB54G 0x000d WUSB54G Wireless Adapter product CISCOLINKSYS WUSB54GP 0x0011 WUSB54GP Wireless Adapter product CISCOLINKSYS USB200MV2 0x0018 USB200M v2 product CISCOLINKSYS HU200TS 0x001a HU200TS Wireless Adapter product CISCOLINKSYS WUSB54GC 0x0020 WUSB54GC product CISCOLINKSYS WUSB54GR 0x0023 WUSB54GR product CISCOLINKSYS WUSBF54G 0x0024 WUSBF54G product CISCOLINKSYS AE1000 0x002f AE1000 product CISCOLINKSYS WUSB6300 0x003f WUSB6300 product CISCOLINKSYS USB3GIGV1 0x0041 USB3GIGV1 USB Ethernet Adapter product CISCOLINKSYS2 RT3070 0x4001 RT3070 product CISCOLINKSYS3 RT3070 0x0101 RT3070 /* Clipsal products */ product CLIPSAL 560884 0x0101 560884 C-Bus Audio Matrix Switch product CLIPSAL 5500PACA 0x0201 5500PACA C-Bus Pascal Automation Controller product CLIPSAL 5800PC 0x0301 5800PC C-Bus Wireless Interface product CLIPSAL 5500PCU 0x0303 5500PCU C-Bus Interface product CLIPSAL 5000CT2 0x0304 5000CT2 C-Bus Touch Screen product CLIPSAL C5000CT2 0x0305 C5000CT2 C-Bus Touch Screen product CLIPSAL L51xx 0x0401 L51xx C-Bus Dimmer /* C-Media products */ product CMEDIA CM6206 0x0102 CM106 compatible sound device /* CMOTECH products */ product CMOTECH CNU510 0x5141 CDMA Technologies USB modem product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem product CMOTECH CGU628 0x6006 CGU-628 product CMOTECH CDMA_MODEM1 0x6280 CDMA Technologies USB modem product CMOTECH DISK 0xf000 disk mode /* Compaq products */ product COMPAQ IPAQPOCKETPC 0x0003 iPAQ PocketPC product COMPAQ PJB100 0x504a Personal Jukebox PJB100 product COMPAQ IPAQLINUX 0x505a iPAQ Linux /* Composite Corp products looks the same as "TANGTOP" */ product COMPOSITE USBPS2 0x0001 USB to PS2 Adaptor /* Conceptronic products */ product CONCEPTRONIC PRISM_GT 0x3762 PrismGT USB 2.0 WLAN product CONCEPTRONIC C11U 0x7100 C11U product CONCEPTRONIC WL210 0x7110 WL-210 product CONCEPTRONIC AR5523_1 0x7801 AR5523 product CONCEPTRONIC AR5523_1_NF 0x7802 AR5523 (no firmware) product CONCEPTRONIC AR5523_2 0x7811 AR5523 product CONCEPTRONIC AR5523_2_NF 0x7812 AR5523 (no firmware) product CONCEPTRONIC2 RTL8192SU_1 0x3300 RTL8192SU product CONCEPTRONIC2 RTL8192SU_2 0x3301 RTL8192SU product CONCEPTRONIC2 RTL8192SU_3 0x3302 RTL8192SU product CONCEPTRONIC2 C54RU 0x3c02 C54RU WLAN product CONCEPTRONIC2 C54RU2 0x3c22 C54RU product CONCEPTRONIC2 RT3070_1 0x3c08 RT3070 product CONCEPTRONIC2 RT3070_2 0x3c11 RT3070 product CONCEPTRONIC2 VIGORN61 0x3c25 VIGORN61 product CONCEPTRONIC2 RT2870_1 0x3c06 RT2870 product CONCEPTRONIC2 RT2870_2 0x3c07 RT2870 product CONCEPTRONIC2 RT2870_7 0x3c09 RT2870 product CONCEPTRONIC2 RT2870_8 0x3c12 RT2870 product CONCEPTRONIC2 RT2870_3 0x3c23 RT2870 product CONCEPTRONIC2 RT2870_4 0x3c25 RT2870 product CONCEPTRONIC2 RT2870_5 0x3c27 RT2870 product CONCEPTRONIC2 RT2870_6 0x3c28 RT2870 /* Connectix products */ product CONNECTIX QUICKCAM 0x0001 QuickCam /* Conect products */ product CONTEC COM1USBH 0x8311 FTDI compatible adapter /* Corega products */ product COREGA ETHER_USB_T 0x0001 Ether USB-T product COREGA FETHER_USB_TX 0x0004 FEther USB-TX product COREGA WLAN_USB_USB_11 0x000c WirelessLAN USB-11 product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS product COREGA WLANUSB 0x0012 Wireless LAN Stick-11 product COREGA FETHER_USB2_TX 0x0017 FEther USB2-TX product COREGA WLUSB_11_KEY 0x001a ULUSB-11 Key product COREGA CGUSBRS232R 0x002a CG-USBRS232R product COREGA CGWLUSB2GL 0x002d CG-WLUSB2GL product COREGA CGWLUSB2GPX 0x002e CG-WLUSB2GPX product COREGA RT2870_1 0x002f RT2870 product COREGA RT2870_2 0x003c RT2870 product COREGA RT2870_3 0x003f RT2870 product COREGA RT3070 0x0041 RT3070 product COREGA CGWLUSB300GNM 0x0042 CG-WLUSB300GNM product COREGA RTL8192SU 0x0047 RTL8192SU product COREGA RTL8192CU 0x0056 RTL8192CU product COREGA WLUSB_11_STICK 0x7613 WLAN USB Stick 11 product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC /* Corsair products */ product CORSAIR K60 0x0a60 Corsair Vengeance K60 keyboard product CORSAIR K70 0x1b09 Corsair Vengeance K70 keyboard product CORSAIR K70_RGB 0x1b13 Corsair K70 RGB Keyboard product CORSAIR STRAFE 0x1b15 Cossair STRAFE Gaming keyboard /* Creative products */ product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player product CREATIVE NOMAD_IIMG 0x4004 Nomad II MG product CREATIVE NOMAD 0x4106 Nomad product CREATIVE2 VOIP_BLASTER 0x0258 Voip Blaster product CREATIVE3 OPTICAL_MOUSE 0x0001 Notebook Optical Mouse /* Cambridge Silicon Radio Ltd. products */ product CSR BT_DONGLE 0x0001 Bluetooth USB dongle product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State /* Chipsbank Microelectronics Co., Ltd */ product CHIPSBANK USBMEMSTICK 0x6025 CBM2080 Flash drive controller product CHIPSBANK USBMEMSTICK1 0x6026 CBM1180 Flash drive controller /* CTX products */ product CTX EX1300 0x9999 Ex1300 hub /* Curitel products */ product CURITEL HX550C 0x1101 CDMA 2000 1xRTT USB modem (HX-550C) product CURITEL HX57XB 0x2101 CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600) product CURITEL PC5740 0x3701 Broadband Wireless modem product CURITEL UM150 0x3711 EVDO modem product CURITEL UM175 0x3714 EVDO modem /* CyberPower products */ product CYBERPOWER 1500CAVRLCD 0x0501 1500CAVRLCD /* CyberTAN Technology products */ product CYBERTAN TG54USB 0x1666 TG54USB product CYBERTAN RT2870 0x1828 RT2870 /* Cypress Semiconductor products */ product CYPRESS MOUSE 0x0001 mouse product CYPRESS THERMO 0x0002 thermometer product CYPRESS WISPY1A 0x0bad MetaGeek Wi-Spy product CYPRESS KBDHUB 0x0101 Keyboard/Hub product CYPRESS FMRADIO 0x1002 FM Radio product CYPRESS IKARILASER 0x121f Ikari Laser SteelSeries ApS product CYPRESS USBRS232 0x5500 USB-RS232 Interface product CYPRESS SLIM_HUB 0x6560 Slim Hub product CYPRESS XX6830XX 0x6830 PATA Storage Device product CYPRESS SILVERSHIELD 0xfd13 Gembird Silver Shield PM /* Daisy Technology products */ product DAISY DMC 0x6901 USB MultiMedia Reader /* Dallas Semiconductor products */ product DALLAS J6502 0x4201 J-6502 speakers /* DataApex products */ product DATAAPEX MULTICOM 0xead6 MultiCom /* Dell products */ product DELL PORT 0x0058 Port Replicator product DELL AIO926 0x5115 Photo AIO Printer 926 product DELL BC02 0x8000 BC02 Bluetooth USB Adapter product DELL PRISM_GT_1 0x8102 PrismGT USB 2.0 WLAN product DELL TM350 0x8103 TrueMobile 350 Bluetooth USB Adapter product DELL PRISM_GT_2 0x8104 PrismGT USB 2.0 WLAN product DELL U5700 0x8114 Dell 5700 3G product DELL U5500 0x8115 Dell 5500 3G product DELL U5505 0x8116 Dell 5505 3G product DELL U5700_2 0x8117 Dell 5700 3G product DELL U5510 0x8118 Dell 5510 3G product DELL U5700_3 0x8128 Dell 5700 3G product DELL U5700_4 0x8129 Dell 5700 3G product DELL U5720 0x8133 Dell 5720 3G product DELL U5720_2 0x8134 Dell 5720 3G product DELL U740 0x8135 Dell U740 CDMA product DELL U5520 0x8136 Dell 5520 3G product DELL U5520_2 0x8137 Dell 5520 3G product DELL U5520_3 0x8138 Dell 5520 3G product DELL U5730 0x8180 Dell 5730 3G product DELL U5730_2 0x8181 Dell 5730 3G product DELL U5730_3 0x8182 Dell 5730 3G product DELL DW700 0x9500 Dell DW700 GPS /* Delorme Paublishing products */ product DELORME EARTHMATE 0x0100 Earthmate GPS /* Desknote products */ product DESKNOTE UCR_61S2B 0x0c55 UCR-61S2B /* Diamond products */ product DIAMOND RIO500USB 0x0001 Rio 500 USB /* Dick Smith Electronics (really C-Net) products */ product DICKSMITH RT2573 0x9022 RT2573 product DICKSMITH CWD854F 0x9032 C-Net CWD-854 rev F /* Digi International products */ product DIGI ACCELEPORT2 0x0002 AccelePort USB 2 product DIGI ACCELEPORT4 0x0004 AccelePort USB 4 product DIGI ACCELEPORT8 0x0008 AccelePort USB 8 /* Digianswer A/S products */ product DIGIANSWER ZIGBEE802154 0x000a ZigBee/802.15.4 MAC /* D-Link products */ /*product DLINK DSBS25 0x0100 DSB-S25 serial*/ product DLINK DUBE100 0x1a00 10/100 Ethernet product DLINK DUBE100C1 0x1a02 DUB-E100 rev C1 product DLINK DSB650TX4 0x200c 10/100 Ethernet product DLINK DWL120E 0x3200 DWL-120 rev E product DLINK RTL8192CU_1 0x3307 RTL8192CU product DLINK RTL8188CU 0x3308 RTL8188CU product DLINK RTL8192CU_2 0x3309 RTL8192CU product DLINK RTL8192CU_3 0x330a RTL8192CU product DLINK DWA131B 0x330d DWA-131 rev B product DLINK DWA125D1 0x330f DWA-125 rev D1 product DLINK DWA123D1 0x3310 DWA-123 rev D1 product DLINK DWA171A1 0x3314 DWA-171 rev A1 product DLINK DWA182C1 0x3315 DWA-182 rev C1 product DLINK DWA180A1 0x3316 DWA-180 rev A1 product DLINK DWA172A1 0x3318 DWA-172 rev A1 product DLINK DWA131E1 0x3319 DWA-131 rev E1 product DLINK DWL122 0x3700 DWL-122 product DLINK DWLG120 0x3701 DWL-G120 product DLINK DWL120F 0x3702 DWL-120 rev F product DLINK DWLAG132 0x3a00 DWL-AG132 product DLINK DWLAG132_NF 0x3a01 DWL-AG132 (no firmware) product DLINK DWLG132 0x3a02 DWL-G132 product DLINK DWLG132_NF 0x3a03 DWL-G132 (no firmware) product DLINK DWLAG122 0x3a04 DWL-AG122 product DLINK DWLAG122_NF 0x3a05 DWL-AG122 (no firmware) product DLINK DWLG122 0x3c00 DWL-G122 b1 Wireless Adapter product DLINK DUBE100B1 0x3c05 DUB-E100 rev B1 product DLINK RT2870 0x3c09 RT2870 product DLINK RT3072 0x3c0a RT3072 product DLINK DWA140B3 0x3c15 DWA-140 rev B3 product DLINK DWA125A3 0x3c19 DWA-125 rev A3 product DLINK DWA160B2 0x3c1a DWA-160 rev B2 product DLINK DWA127 0x3c1b DWA-127 Wireless Adapter product DLINK DWA162 0x3c1f DWA-162 Wireless Adapter product DLINK DWA140D1 0x3c20 DWA-140 rev D1 product DLINK DSB650C 0x4000 10Mbps Ethernet product DLINK DSB650TX1 0x4001 10/100 Ethernet product DLINK DSB650TX 0x4002 10/100 Ethernet product DLINK DSB650TX_PNA 0x4003 1/10/100 Ethernet product DLINK DSB650TX3 0x400b 10/100 Ethernet product DLINK DSB650TX2 0x4102 10/100 Ethernet product DLINK DUB1312 0x4a00 10/100/1000 Ethernet product DLINK DWM157 0x7d02 DWM-157 product DLINK DWR510 0x7e12 DWR-510 product DLINK DWM222 0x7e35 DWM-222 product DLINK DWM157_CD 0xa707 DWM-157 CD-ROM Mode product DLINK DWR510_CD 0xa805 DWR-510 CD-ROM Mode product DLINK DWM222_CD 0xab00 DWM-222 CD-ROM Mode product DLINK DSB650 0xabc1 10/100 Ethernet product DLINK DUBH7 0xf103 DUB-H7 USB 2.0 7-Port Hub product DLINK2 RTL8192SU_1 0x3300 RTL8192SU product DLINK2 RTL8192SU_2 0x3302 RTL8192SU product DLINK2 DWA131A1 0x3303 DWA-131 A1 product DLINK2 DWA160A2 0x3a09 DWA-160 A2 product DLINK2 DWA120 0x3a0c DWA-120 product DLINK2 DWA120_NF 0x3a0d DWA-120 (no firmware) product DLINK2 DWA130D1 0x3a0f DWA-130 D1 product DLINK2 DWLG122C1 0x3c03 DWL-G122 c1 product DLINK2 WUA1340 0x3c04 WUA-1340 product DLINK2 DWA111 0x3c06 DWA-111 product DLINK2 DWA110 0x3c07 DWA-110 product DLINK2 RT2870_1 0x3c09 RT2870 product DLINK2 RT3072 0x3c0a RT3072 product DLINK2 RT3072_1 0x3c0b RT3072 product DLINK2 RT3070_1 0x3c0d RT3070 product DLINK2 RT3070_2 0x3c0e RT3070 product DLINK2 RT3070_3 0x3c0f RT3070 product DLINK2 DWA160A1 0x3c10 DWA-160 A1 product DLINK2 RT2870_2 0x3c11 RT2870 product DLINK2 DWA130 0x3c13 DWA-130 product DLINK2 RT3070_4 0x3c15 RT3070 product DLINK2 RT3070_5 0x3c16 RT3070 product DLINK3 DWM652 0x3e04 DWM-652 /* DisplayLink products */ product DISPLAYLINK LCD4300U 0x01ba LCD-4300U product DISPLAYLINK LCD8000U 0x01bb LCD-8000U product DISPLAYLINK LD220 0x0100 Samsung LD220 product DISPLAYLINK GUC2020 0x0059 IOGEAR DVI GUC2020 product DISPLAYLINK VCUD60 0x0136 Rextron DVI product DISPLAYLINK CONV 0x0138 StarTech CONV-USB2DVI product DISPLAYLINK DLDVI 0x0141 DisplayLink DVI product DISPLAYLINK VGA10 0x015a CMP-USBVGA10 product DISPLAYLINK WSDVI 0x0198 WS Tech DVI product DISPLAYLINK EC008 0x019b EasyCAP008 DVI product DISPLAYLINK HPDOCK 0x01d4 HP USB Docking product DISPLAYLINK NL571 0x01d7 HP USB DVI product DISPLAYLINK M01061 0x01e2 Lenovo DVI product DISPLAYLINK SWDVI 0x024c SUNWEIT DVI product DISPLAYLINK NBDOCK 0x0215 VideoHome NBdock1920 product DISPLAYLINK LUM70 0x02a9 Lilliput UM-70 product DISPLAYLINK UM7X0 0x401a nanovision MiMo product DISPLAYLINK LT1421 0x03e0 Lenovo ThinkVision LT1421 product DISPLAYLINK POLARIS2 0x0117 Polaris2 USB dock product DISPLAYLINK PLUGABLE 0x0377 Plugable docking station product DISPLAYLINK ITEC 0x02e9 i-tec USB 2.0 Docking Station /* DMI products */ product DMI CFSM_RW 0xa109 CF/SM Reader/Writer product DMI DISK 0x2bcf Generic Disk /* DrayTek products */ product DRAYTEK VIGOR550 0x0550 Vigor550 /* Dream Link products */ product DREAMLINK DL100B 0x0004 USB Webmail Notifier /* dresden elektronik products */ product DRESDENELEKTRONIK SENSORTERMINALBOARD 0x0001 SensorTerminalBoard product DRESDENELEKTRONIK WIRELESSHANDHELDTERMINAL 0x0004 Wireless Handheld Terminal product DRESDENELEKTRONIK DE_RFNODE 0x001c deRFnode product DRESDENELEKTRONIK LEVELSHIFTERSTICKLOWCOST 0x0022 Levelshifter Stick Low Cost /* DYMO */ product DYMO LABELMANAGERPNP 0x1001 DYMO LabelManager PnP /* Dynastream Innovations */ product DYNASTREAM ANTDEVBOARD 0x1003 ANT dev board product DYNASTREAM ANT2USB 0x1004 ANT2USB product DYNASTREAM ANTDEVBOARD2 0x1006 ANT dev board /* Edimax products */ product EDIMAX EW7318USG 0x7318 USB Wireless dongle product EDIMAX RTL8192SU_1 0x7611 RTL8192SU product EDIMAX RTL8192SU_2 0x7612 RTL8192SU product EDIMAX EW7622UMN 0x7622 EW-7622UMn product EDIMAX RT2870_1 0x7711 RT2870 product EDIMAX EW7717 0x7717 EW-7717 product EDIMAX EW7718 0x7718 EW-7718 product EDIMAX EW7733UND 0x7733 EW-7733UnD product EDIMAX EW7811UN 0x7811 EW-7811Un product EDIMAX RTL8192CU 0x7822 RTL8192CU product EDIMAX EW7811UTC_1 0xa811 EW-7811UTC product EDIMAX EW7811UTC_2 0xa812 EW-7811UTC product EDIMAX EW7822UAC 0xa822 EW-7822UAC /* eGalax Products */ product EGALAX TPANEL 0x0001 Touch Panel product EGALAX TPANEL2 0x0002 Touch Panel product EGALAX2 TPANEL 0x0001 Touch Panel /* EGO Products */ product EGO DUMMY 0x0000 Dummy Product product EGO M4U 0x1020 ESI M4U /* Eicon Networks */ product EICON DIVA852 0x4905 Diva 852 ISDN TA /* EIZO products */ product EIZO HUB 0x0000 hub product EIZO MONITOR 0x0001 monitor /* ELCON Systemtechnik products */ product ELCON PLAN 0x0002 Goldpfeil P-LAN /* Elecom products */ product ELECOM MOUSE29UO 0x0002 mouse 29UO product ELECOM LDUSBTX0 0x200c LD-USB/TX product ELECOM LDUSBTX1 0x4002 LD-USB/TX product ELECOM LDUSBLTX 0x4005 LD-USBL/TX product ELECOM WDC150SU2M 0x4008 WDC-150SU2M product ELECOM LDUSBTX2 0x400b LD-USB/TX product ELECOM LDUSB20 0x4010 LD-USB20 product ELECOM UCSGT 0x5003 UC-SGT product ELECOM UCSGT0 0x5004 UC-SGT product ELECOM LDUSBTX3 0xabc1 LD-USB/TX /* Elektor products */ product ELEKTOR FT323R 0x0005 FTDI compatible adapter /* Elsa products */ product ELSA MODEM1 0x2265 ELSA Modem Board product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet /* ELV products */ product ELV USBI2C 0xe00f USB-I2C interface /* EMS products */ product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter /* Emtec products */ product EMTEC RUF2PS 0x2240 Flash Drive /* Encore products */ product ENCORE RT3070_1 0x1480 RT3070 product ENCORE RT3070_2 0x14a1 RT3070 product ENCORE RT3070_3 0x14a9 RT3070 /* Entrega products */ product ENTREGA 1S 0x0001 1S serial product ENTREGA 2S 0x0002 2S serial product ENTREGA 1S25 0x0003 1S25 serial product ENTREGA 4S 0x0004 4S serial product ENTREGA E45 0x0005 E45 Ethernet product ENTREGA CENTRONICS 0x0006 Parallel Port product ENTREGA XX1 0x0008 Ethernet product ENTREGA 1S9 0x0093 1S9 serial product ENTREGA EZUSB 0x8000 EZ-USB /*product ENTREGA SERIAL 0x8001 DB25 Serial*/ product ENTREGA 2U4S 0x8004 2U4S serial/usb hub product ENTREGA XX2 0x8005 Ethernet /*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/ /* Epson products */ product EPSON PRINTER1 0x0001 USB Printer product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac product EPSON PRINTER3 0x0003 ISD USB Smart Cable product EPSON PRINTER5 0x0005 USB Printer product EPSON 636 0x0101 Perfection 636U / 636Photo scanner product EPSON 610 0x0103 Perfection 610 scanner product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner product EPSON 1600 0x0107 Expression 1600 scanner product EPSON 1640 0x010a Perfection 1640SU scanner product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner product EPSON 640U 0x010c Perfection 640U scanner product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner product EPSON 1650 0x0110 Perfection 1650 scanner product EPSON GT9700F 0x0112 GT-9700F scanner product EPSON GT9300UF 0x011b GT-9300UF scanner product EPSON 3200 0x011c Perfection 3200 scanner product EPSON 1260 0x011d Perfection 1260 scanner product EPSON 1660 0x011e Perfection 1660 scanner product EPSON 1670 0x011f Perfection 1670 scanner product EPSON 1270 0x0120 Perfection 1270 scanner product EPSON 2480 0x0121 Perfection 2480 scanner product EPSON 3590 0x0122 Perfection 3590 scanner product EPSON 4990 0x012a Perfection 4990 Photo scanner product EPSON CRESSI_EDY 0x0521 Cressi Edy diving computer product EPSON N2ITION3 0x0522 Zeagle N2iTion3 diving computer product EPSON STYLUS_875DC 0x0601 Stylus Photo 875DC Card Reader product EPSON STYLUS_895 0x0602 Stylus Photo 895 Card Reader product EPSON CX5400 0x0808 CX5400 scanner product EPSON 3500 0x080e CX-3500/3600/3650 MFP product EPSON RX425 0x080f Stylus Photo RX425 scanner product EPSON DX3800 0x0818 CX3700/CX3800/DX38x0 MFP scanner product EPSON 4800 0x0819 CX4700/CX4800/DX48x0 MFP scanner product EPSON 4200 0x0820 CX4100/CX4200/DX4200 MFP scanner product EPSON 5000 0x082b CX4900/CX5000/DX50x0 MFP scanner product EPSON 6000 0x082e CX5900/CX6000/DX60x0 MFP scanner product EPSON DX4000 0x082f DX4000 MFP scanner product EPSON DX7400 0x0838 CX7300/CX7400/DX7400 MFP scanner product EPSON DX8400 0x0839 CX8300/CX8400/DX8400 MFP scanner product EPSON SX100 0x0841 SX100/NX100 MFP scanner product EPSON NX300 0x0848 NX300 MFP scanner product EPSON SX200 0x0849 SX200/SX205 MFP scanner product EPSON SX400 0x084a SX400/NX400/TX400 MFP scanner /* e-TEK Labs products */ product ETEK 1COM 0x8007 Serial /* Evolution products */ product EVOLUTION ER1 0x0300 FTDI compatible adapter product EVOLUTION HYBRID 0x0302 FTDI compatible adapter product EVOLUTION RCM4 0x0303 FTDI compatible adapter /* Extended Systems products */ product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA /* Falcom products */ product FALCOM TWIST 0x0001 USB GSM/GPRS Modem product FALCOM SAMBA 0x0005 FTDI compatible adapter /* FEIYA products */ product FEIYA DUMMY 0x0000 Dummy product product FEIYA 5IN1 0x1132 5-in-1 Card Reader product FEIYA ELANGO 0x6200 MicroSDHC Card Reader product FEIYA AC110 0x6300 AC-110 Card Reader /* FeiXun Communication products */ product FEIXUN RTL8188CU 0x0090 RTL8188CU product FEIXUN RTL8192CU 0x0091 RTL8192CU /* Festo */ product FESTO CPX_USB 0x0102 CPX-USB product FESTO CMSP 0x0501 CMSP /* Fiberline */ product FIBERLINE WL430U 0x6003 WL-430U /* FIC / OpenMoko */ product FIC NEO1973_DEBUG 0x5118 FTDI compatible adapter /* Fossil, Inc products */ product FOSSIL WRISTPDA 0x0002 Wrist PDA /* Foxconn products */ product FOXCONN TCOM_TC_300 0xe000 T-Com TC 300 product FOXCONN PIRELLI_DP_L10 0xe003 Pirelli DP-L10 /* Freecom products */ product FREECOM DVD 0xfc01 DVD drive product FREECOM HDD 0xfc05 Classic SL Hard Drive /* Fujitsu Siemens Computers products */ product FSC E5400 0x1009 PrismGT USB 2.0 WLAN /* Future Technology Devices products */ product FTDI SCX8_USB_PHOENIX 0x5259 SCx8 USB Phoenix interface product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial product FTDI SERIAL_8U232AM4 0x6004 8U232AM Serial product FTDI SERIAL_232RL 0x6006 FT232RL Serial product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial product FTDI 232H 0x6014 FTDI compatible adapter product FTDI 232EX 0x6015 FTDI compatible adapter product FTDI SERIAL_2232D 0x9e90 FT2232D Dual port Serial product FTDI SERIAL_4232H 0x6011 FT4232H Quad port Serial product FTDI XDS100V2 0xa6d0 TI XDS100V1/V2 and early Beaglebones product FTDI XDS100V3 0xa6d1 TI XDS100V3 product FTDI KTLINK 0xbbe2 KT-LINK Embedded Hackers Multitool product FTDI TURTELIZER2 0xbdc8 egnite Turtelizer 2 JTAG/RS232 Adapter /* Gude Analog- und Digitalsysteme products also uses FTDI's id: */ product FTDI TACTRIX_OPENPORT_13M 0xcc48 OpenPort 1.3 Mitsubishi product FTDI TACTRIX_OPENPORT_13S 0xcc49 OpenPort 1.3 Subaru product FTDI TACTRIX_OPENPORT_13U 0xcc4a OpenPort 1.3 Universal product FTDI GAMMASCOUT 0xd678 Gamma-Scout product FTDI KBS 0xe6c8 Pyramid KBS USB LCD product FTDI EISCOU 0xe888 Expert ISDN Control USB product FTDI UOPTBR 0xe889 USB-RS232 OptoBridge product FTDI EMCU2D 0xe88a Expert mouseCLOCK USB II product FTDI PCMSFU 0xe88b Precision Clock MSF USB product FTDI EMCU2H 0xe88c Expert mouseCLOCK USB II HBG product FTDI MAXSTREAM 0xee18 Maxstream PKG-U product FTDI USB_UIRT 0xf850 USB-UIRT product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3 product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5 product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD product FTDI CFA_635 0xfc0d Crystalfontz CFA-635 USB LCD product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation /* Commerzielle und Technische Informationssysteme GmbH products */ product FTDI CTI_USB_NANO_485 0xf60b CTI USB-Nano 485 product FTDI CTI_USB_MINI_485 0xf608 CTI USB-Mini 485 /* Other products */ product FTDI 232RL 0xfbfa FTDI compatible adapter product FTDI 4N_GALAXY_DE_1 0xf3c0 FTDI compatible adapter product FTDI 4N_GALAXY_DE_2 0xf3c1 FTDI compatible adapter product FTDI 4N_GALAXY_DE_3 0xf3c2 FTDI compatible adapter product FTDI 8U232AM_ALT 0x6006 FTDI compatible adapter product FTDI ACCESSO 0xfad0 FTDI compatible adapter product FTDI ACG_HFDUAL 0xdd20 FTDI compatible adapter product FTDI ACTIVE_ROBOTS 0xe548 FTDI compatible adapter product FTDI ACTZWAVE 0xf2d0 FTDI compatible adapter product FTDI AMC232 0xff00 FTDI compatible adapter product FTDI ARTEMIS 0xdf28 FTDI compatible adapter product FTDI ASK_RDR400 0xc991 FTDI compatible adapter product FTDI ATIK_ATK16 0xdf30 FTDI compatible adapter product FTDI ATIK_ATK16C 0xdf32 FTDI compatible adapter product FTDI ATIK_ATK16HR 0xdf31 FTDI compatible adapter product FTDI ATIK_ATK16HRC 0xdf33 FTDI compatible adapter product FTDI ATIK_ATK16IC 0xdf35 FTDI compatible adapter product FTDI BCS_SE923 0xfb99 FTDI compatible adapter product FTDI CANDAPTER 0x9f80 FTDI compatible adapter product FTDI CANUSB 0xffa8 FTDI compatible adapter product FTDI CCSICDU20_0 0xf9d0 FTDI compatible adapter product FTDI CCSICDU40_1 0xf9d1 FTDI compatible adapter product FTDI CCSICDU64_4 0xf9d4 FTDI compatible adapter product FTDI CCSLOAD_N_GO_3 0xf9d3 FTDI compatible adapter product FTDI CCSMACHX_2 0xf9d2 FTDI compatible adapter product FTDI CCSPRIME8_5 0xf9d5 FTDI compatible adapter product FTDI CHAMSYS_24_MASTER_WING 0xdaf8 FTDI compatible adapter product FTDI CHAMSYS_MAXI_WING 0xdafd FTDI compatible adapter product FTDI CHAMSYS_MEDIA_WING 0xdafe FTDI compatible adapter product FTDI CHAMSYS_MIDI_TIMECODE 0xdafb FTDI compatible adapter product FTDI CHAMSYS_MINI_WING 0xdafc FTDI compatible adapter product FTDI CHAMSYS_PC_WING 0xdaf9 FTDI compatible adapter product FTDI CHAMSYS_USB_DMX 0xdafa FTDI compatible adapter product FTDI CHAMSYS_WING 0xdaff FTDI compatible adapter product FTDI COM4SM 0xd578 FTDI compatible adapter product FTDI CONVERTER_0 0xd388 FTDI compatible adapter product FTDI CONVERTER_1 0xd389 FTDI compatible adapter product FTDI CONVERTER_2 0xd38a FTDI compatible adapter product FTDI CONVERTER_3 0xd38b FTDI compatible adapter product FTDI CONVERTER_4 0xd38c FTDI compatible adapter product FTDI CONVERTER_5 0xd38d FTDI compatible adapter product FTDI CONVERTER_6 0xd38e FTDI compatible adapter product FTDI CONVERTER_7 0xd38f FTDI compatible adapter product FTDI DMX4ALL 0xc850 FTDI compatible adapter product FTDI DOMINTELL_DGQG 0xef50 FTDI compatible adapter product FTDI DOMINTELL_DUSB 0xef51 FTDI compatible adapter product FTDI DOTEC 0x9868 FTDI compatible adapter product FTDI ECLO_COM_1WIRE 0xea90 FTDI compatible adapter product FTDI ECO_PRO_CDS 0xe520 FTDI compatible adapter product FTDI ELSTER_UNICOM 0xe700 FTDI compatible adapter product FTDI ELV_ALC8500 0xf06e FTDI compatible adapter product FTDI ELV_CLI7000 0xfb59 FTDI compatible adapter product FTDI ELV_CSI8 0xe0f0 FTDI compatible adapter product FTDI ELV_EC3000 0xe006 FTDI compatible adapter product FTDI ELV_EM1000DL 0xe0f1 FTDI compatible adapter product FTDI ELV_EM1010PC 0xe0ef FTDI compatible adapter product FTDI ELV_FEM 0xe00a FTDI compatible adapter product FTDI ELV_FHZ1000PC 0xf06f FTDI compatible adapter product FTDI ELV_FHZ1300PC 0xe0e8 FTDI compatible adapter product FTDI ELV_FM3RX 0xe0ed FTDI compatible adapter product FTDI ELV_FS20SIG 0xe0f4 FTDI compatible adapter product FTDI ELV_HS485 0xe0ea FTDI compatible adapter product FTDI ELV_KL100 0xe002 FTDI compatible adapter product FTDI ELV_MSM1 0xe001 FTDI compatible adapter product FTDI ELV_PCD200 0xf06c FTDI compatible adapter product FTDI ELV_PCK100 0xe0f2 FTDI compatible adapter product FTDI ELV_PPS7330 0xfb5c FTDI compatible adapter product FTDI ELV_RFP500 0xe0f3 FTDI compatible adapter product FTDI ELV_T1100 0xf06b FTDI compatible adapter product FTDI ELV_TFD128 0xe0ec FTDI compatible adapter product FTDI ELV_TFM100 0xfb5d FTDI compatible adapter product FTDI ELV_TWS550 0xe009 FTDI compatible adapter product FTDI ELV_UAD8 0xf068 FTDI compatible adapter product FTDI ELV_UDA7 0xf069 FTDI compatible adapter product FTDI ELV_UDF77 0xfb5e FTDI compatible adapter product FTDI ELV_UIO88 0xfb5f FTDI compatible adapter product FTDI ELV_ULA200 0xf06d FTDI compatible adapter product FTDI ELV_UM100 0xfb5a FTDI compatible adapter product FTDI ELV_UMS100 0xe0eb FTDI compatible adapter product FTDI ELV_UO100 0xfb5b FTDI compatible adapter product FTDI ELV_UR100 0xfb58 FTDI compatible adapter product FTDI ELV_USI2 0xf06a FTDI compatible adapter product FTDI ELV_USR 0xe000 FTDI compatible adapter product FTDI ELV_UTP8 0xe0f5 FTDI compatible adapter product FTDI ELV_WS300PC 0xe0f6 FTDI compatible adapter product FTDI ELV_WS444PC 0xe0f7 FTDI compatible adapter product FTDI ELV_WS500 0xe0e9 FTDI compatible adapter product FTDI ELV_WS550 0xe004 FTDI compatible adapter product FTDI ELV_WS777 0xe0ee FTDI compatible adapter product FTDI ELV_WS888 0xe008 FTDI compatible adapter product FTDI FUTURE_0 0xf44a FTDI compatible adapter product FTDI FUTURE_1 0xf44b FTDI compatible adapter product FTDI FUTURE_2 0xf44c FTDI compatible adapter product FTDI GENERIC 0x9378 FTDI compatible adapter product FTDI GUDEADS_E808 0xe808 FTDI compatible adapter product FTDI GUDEADS_E809 0xe809 FTDI compatible adapter product FTDI GUDEADS_E80A 0xe80a FTDI compatible adapter product FTDI GUDEADS_E80B 0xe80b FTDI compatible adapter product FTDI GUDEADS_E80C 0xe80c FTDI compatible adapter product FTDI GUDEADS_E80D 0xe80d FTDI compatible adapter product FTDI GUDEADS_E80E 0xe80e FTDI compatible adapter product FTDI GUDEADS_E80F 0xe80f FTDI compatible adapter product FTDI GUDEADS_E88D 0xe88d FTDI compatible adapter product FTDI GUDEADS_E88E 0xe88e FTDI compatible adapter product FTDI GUDEADS_E88F 0xe88f FTDI compatible adapter product FTDI HD_RADIO 0x937c FTDI compatible adapter product FTDI HO720 0xed72 FTDI compatible adapter product FTDI HO730 0xed73 FTDI compatible adapter product FTDI HO820 0xed74 FTDI compatible adapter product FTDI HO870 0xed71 FTDI compatible adapter product FTDI IBS_APP70 0xff3d FTDI compatible adapter product FTDI IBS_PCMCIA 0xff3a FTDI compatible adapter product FTDI IBS_PEDO 0xff3e FTDI compatible adapter product FTDI IBS_PICPRO 0xff39 FTDI compatible adapter product FTDI IBS_PK1 0xff3b FTDI compatible adapter product FTDI IBS_PROD 0xff3f FTDI compatible adapter product FTDI IBS_RS232MON 0xff3c FTDI compatible adapter product FTDI IBS_US485 0xff38 FTDI compatible adapter product FTDI IPLUS 0xd070 FTDI compatible adapter product FTDI IPLUS2 0xd071 FTDI compatible adapter product FTDI IRTRANS 0xfc60 FTDI compatible adapter product FTDI LENZ_LIUSB 0xd780 FTDI compatible adapter product FTDI LM3S_DEVEL_BOARD 0xbcd8 FTDI compatible adapter product FTDI LM3S_EVAL_BOARD 0xbcd9 FTDI compatible adapter product FTDI LM3S_ICDI_B_BOARD 0xbcda FTDI compatible adapter product FTDI MASTERDEVEL2 0xf449 FTDI compatible adapter product FTDI MHAM_DB9 0xeeed FTDI compatible adapter product FTDI MHAM_IC 0xeeec FTDI compatible adapter product FTDI MHAM_KW 0xeee8 FTDI compatible adapter product FTDI MHAM_RS232 0xeeee FTDI compatible adapter product FTDI MHAM_Y6 0xeeea FTDI compatible adapter product FTDI MHAM_Y8 0xeeeb FTDI compatible adapter product FTDI MHAM_Y9 0xeeef FTDI compatible adapter product FTDI MHAM_YS 0xeee9 FTDI compatible adapter product FTDI MICRO_CHAMELEON 0xcaa0 FTDI compatible adapter product FTDI MTXORB_5 0xfa05 FTDI compatible adapter product FTDI MTXORB_6 0xfa06 FTDI compatible adapter product FTDI NXTCAM 0xabb8 FTDI compatible adapter product FTDI OCEANIC 0xf460 FTDI compatible adapter product FTDI OOCDLINK 0xbaf8 FTDI compatible adapter product FTDI OPENDCC 0xbfd8 FTDI compatible adapter product FTDI OPENDCC_GATEWAY 0xbfdb FTDI compatible adapter product FTDI OPENDCC_GBM 0xbfdc FTDI compatible adapter product FTDI OPENDCC_SNIFFER 0xbfd9 FTDI compatible adapter product FTDI OPENDCC_THROTTLE 0xbfda FTDI compatible adapter product FTDI PCDJ_DAC2 0xfa88 FTDI compatible adapter product FTDI PERLE_ULTRAPORT 0xf0c0 FTDI compatible adapter product FTDI PHI_FISCO 0xe40b FTDI compatible adapter product FTDI PIEGROUP 0xf208 FTDI compatible adapter product FTDI PROPOX_JTAGCABLEII 0xd738 FTDI compatible adapter product FTDI R2000KU_TRUE_RNG 0xfb80 FTDI compatible adapter product FTDI R2X0 0xfc71 FTDI compatible adapter product FTDI RELAIS 0xfa10 FTDI compatible adapter product FTDI REU_TINY 0xed22 FTDI compatible adapter product FTDI RMP200 0xe729 FTDI compatible adapter product FTDI RM_CANVIEW 0xfd60 FTDI compatible adapter product FTDI RRCIRKITS_LOCOBUFFER 0xc7d0 FTDI compatible adapter product FTDI SCIENCESCOPE_HS_LOGBOOK 0xff1d FTDI compatible adapter product FTDI SCIENCESCOPE_LOGBOOKML 0xff18 FTDI compatible adapter product FTDI SCIENCESCOPE_LS_LOGBOOK 0xff1c FTDI compatible adapter product FTDI SCS_DEVICE_0 0xd010 FTDI compatible adapter product FTDI SCS_DEVICE_1 0xd011 FTDI compatible adapter product FTDI SCS_DEVICE_2 0xd012 FTDI compatible adapter product FTDI SCS_DEVICE_3 0xd013 FTDI compatible adapter product FTDI SCS_DEVICE_4 0xd014 FTDI compatible adapter product FTDI SCS_DEVICE_5 0xd015 FTDI compatible adapter product FTDI SCS_DEVICE_6 0xd016 FTDI compatible adapter product FTDI SCS_DEVICE_7 0xd017 FTDI compatible adapter product FTDI SDMUSBQSS 0xf448 FTDI compatible adapter product FTDI SIGNALYZER_SH2 0xbca2 FTDI compatible adapter product FTDI SIGNALYZER_SH4 0xbca4 FTDI compatible adapter product FTDI SIGNALYZER_SLITE 0xbca1 FTDI compatible adapter product FTDI SIGNALYZER_ST 0xbca0 FTDI compatible adapter product FTDI SPECIAL_1 0xfc70 FTDI compatible adapter product FTDI SPECIAL_3 0xfc72 FTDI compatible adapter product FTDI SPECIAL_4 0xfc73 FTDI compatible adapter product FTDI SPROG_II 0xf0c8 FTDI compatible adapter product FTDI SR_RADIO 0x9379 FTDI compatible adapter product FTDI SUUNTO_SPORTS 0xf680 FTDI compatible adapter product FTDI TAVIR_STK500 0xfa33 FTDI compatible adapter product FTDI TERATRONIK_D2XX 0xec89 FTDI compatible adapter product FTDI TERATRONIK_VCP 0xec88 FTDI compatible adapter product FTDI THORLABS 0xfaf0 FTDI compatible adapter product FTDI TIAO 0x8a98 FTDI compatible adapter product FTDI TNC_X 0xebe0 FTDI compatible adapter product FTDI TTUSB 0xff20 FTDI compatible adapter product FTDI USBX_707 0xf857 FTDI compatible adapter product FTDI USINT_CAT 0xb810 FTDI compatible adapter product FTDI USINT_RS232 0xb812 FTDI compatible adapter product FTDI USINT_WKEY 0xb811 FTDI compatible adapter product FTDI VARDAAN 0xf070 FTDI compatible adapter product FTDI VNHCPCUSB_D 0xfe38 FTDI compatible adapter product FTDI WESTREX_MODEL_777 0xdc00 FTDI compatible adapter product FTDI WESTREX_MODEL_8900F 0xdc01 FTDI compatible adapter product FTDI XF_547 0xfc0a FTDI compatible adapter product FTDI XF_640 0xfc0e FTDI compatible adapter product FTDI XF_642 0xfc0f FTDI compatible adapter product FTDI XM_RADIO 0x937a FTDI compatible adapter product FTDI YEI_SERVOCENTER31 0xe050 FTDI compatible adapter /* Fuji photo products */ product FUJIPHOTO MASS0100 0x0100 Mass Storage /* Fujitsu protducts */ product FUJITSU AH_F401U 0x105b AH-F401U Air H device /* Fujitsu-Siemens protducts */ product FUJITSUSIEMENS SCR 0x0009 Fujitsu-Siemens SCR USB Reader /* Garmin products */ product GARMIN FORERUNNER230 0x086d ForeRunner 230 product GARMIN IQUE_3600 0x0004 iQue 3600 /* Gemalto products */ product GEMALTO PROXPU 0x5501 Prox-PU/CU RFID Card Reader /* General Instruments (Motorola) products */ product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem /* Genesys Logic products */ product GENESYS GL620USB 0x0501 GL620USB Host-Host interface product GENESYS GL650 0x0604 GL650 HUB product GENESYS GL606 0x0606 GL606 USB 2.0 HUB product GENESYS GL850G 0x0608 GL850G USB 2.0 HUB product GENESYS GL3520_2 0x0610 GL3520 4-Port USB 2.0 DataPath product GENESYS GL3520_SS 0x0616 GL3520 4-Port USB 3.0 DataPath product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2 product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge product GENESYS GL3233 0x0743 GL3233 USB 3.0 AiO Card Reader product GENESYS GL641USB_2 0x0760 GL641USB 6-in-1 Card Reader /* GIGABYTE products */ product GIGABYTE GN54G 0x8001 GN-54G product GIGABYTE GNBR402W 0x8002 GN-BR402W product GIGABYTE GNWLBM101 0x8003 GN-WLBM101 product GIGABYTE GNWBKG 0x8007 GN-WBKG product GIGABYTE GNWB01GS 0x8008 GN-WB01GS product GIGABYTE GNWI05GS 0x800a GN-WI05GS /* Gigaset products */ product GIGASET WLAN 0x0701 WLAN product GIGASET SMCWUSBTG 0x0710 SMCWUSBT-G product GIGASET SMCWUSBTG_NF 0x0711 SMCWUSBT-G (no firmware) product GIGASET AR5523 0x0712 AR5523 product GIGASET AR5523_NF 0x0713 AR5523 (no firmware) product GIGASET RT2573 0x0722 RT2573 product GIGASET RT3070_1 0x0740 RT3070 product GIGASET RT3070_2 0x0744 RT3070 product GIGABYTE RT2870_1 0x800b RT2870 product GIGABYTE GNWB31N 0x800c GN-WB31N product GIGABYTE GNWB32L 0x800d GN-WB32L /* Global Sun Technology product */ product GLOBALSUN AR5523_1 0x7801 AR5523 product GLOBALSUN AR5523_1_NF 0x7802 AR5523 (no firmware) product GLOBALSUN AR5523_2 0x7811 AR5523 product GLOBALSUN AR5523_2_NF 0x7812 AR5523 (no firmware) /* Globespan products */ product GLOBESPAN MODEM_1 0x1329 USB Modem product GLOBESPAN PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN product GLOBESPAN PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN /* G.Mate, Inc products */ product GMATE YP3X00 0x1001 YP3X00 PDA /* GN Otometrics */ product GNOTOMETRICS USB 0x0010 FTDI compatible adapter /* GoHubs products */ product GOHUBS GOCOM232 0x1001 GoCOM232 Serial /* Good Way Technology products */ product GOODWAY GWUSB2E 0x6200 GWUSB2E product GOODWAY RT2573 0xc019 RT2573 /* Google products */ product GOOGLE NEXUSONE 0x4e11 Nexus One /* Gravis products */ product GRAVIS GAMEPADPRO 0x4001 GamePad Pro /* GREENHOUSE products */ product GREENHOUSE KANA21 0x0001 CF-writer with MP3 /* Griffin Technology */ product GRIFFIN IMATE 0x0405 iMate, ADB Adapter /* Guillemot Corporation */ product GUILLEMOT DALEADER 0xa300 DA Leader product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN product GUILLEMOT HWGUSB254LB 0xe010 HWGUSB2-54-LB product GUILLEMOT HWGUSB254V2AP 0xe020 HWGUSB2-54V2-AP product GUILLEMOT HWNU300 0xe030 HWNU-300 product GUILLEMOT HWNUM300 0xe031 HWNUm-300 product GUILLEMOT HWGUN54 0xe032 HWGUn-54 product GUILLEMOT HWNUP150 0xe033 HWNUP-150 /* Hagiwara products */ product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader product HAGIWARA FG 0x0005 FlashGate /* HAL Corporation products */ product HAL IMR001 0x0011 Crossam2+USB IR commander /* Handspring, Inc. */ product HANDSPRING VISOR 0x0100 Handspring Visor product HANDSPRING TREO 0x0200 Handspring Treo product HANDSPRING TREO600 0x0300 Handspring Treo 600 /* Hauppauge Computer Works */ product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM product HAUPPAUGE2 NOVAT500 0x9580 NovaT 500Stick /* Hawking Technologies products */ product HAWKING RT2870_1 0x0001 RT2870 product HAWKING RT2870_2 0x0003 RT2870 product HAWKING HWUN2 0x0009 HWUN2 product HAWKING RT3070 0x000b RT3070 product HAWKING RTL8192CU 0x0019 RTL8192CU product HAWKING UF100 0x400c 10/100 USB Ethernet product HAWKING RTL8192SU_1 0x0015 RTL8192SU product HAWKING RTL8192SU_2 0x0016 RTL8192SU product HAWKING HD65U 0x0023 HD65U /* HID Global GmbH products */ product HIDGLOBAL CM2020 0x0596 Omnikey Cardman 2020 product HIDGLOBAL CM6020 0x1784 Omnikey Cardman 6020 /* Hitachi, Ltd. products */ product HITACHI DVDCAM_DZ_MV100A 0x0004 DVD-CAM DZ-MV100A Camcorder product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface /* Holtek products */ product HOLTEK F85 0xa030 Holtek USB gaming keyboard /* Honeywell */ product HONEYWELL HGI80 0x0102 Honeywell HGI80 Wireless USB Gateway /* HP products */ product HP 895C 0x0004 DeskJet 895C product HP 4100C 0x0101 Scanjet 4100C product HP S20 0x0102 Photosmart S20 product HP 880C 0x0104 DeskJet 880C product HP 4200C 0x0105 ScanJet 4200C product HP CDWRITERPLUS 0x0107 CD-Writer Plus product HP KBDHUB 0x010c Multimedia Keyboard Hub product HP G55XI 0x0111 OfficeJet G55xi product HP HN210W 0x011c HN210W 802.11b WLAN product HP 49GPLUS 0x0121 49g+ graphing calculator product HP 6200C 0x0201 ScanJet 6200C product HP S20b 0x0202 PhotoSmart S20 product HP 815C 0x0204 DeskJet 815C product HP 3300C 0x0205 ScanJet 3300C product HP CDW8200 0x0207 CD-Writer Plus 8200e product HP MMKEYB 0x020c Multimedia keyboard product HP 1220C 0x0212 DeskJet 1220C product HP UN2420_QDL 0x241d UN2420 QDL Firmware Loader product HP UN2420 0x251d UN2420 WWAN/GPS Module product HP 810C 0x0304 DeskJet 810C/812C product HP 4300C 0x0305 Scanjet 4300C product HP CDW4E 0x0307 CD-Writer+ CD-4e product HP G85XI 0x0311 OfficeJet G85xi product HP 1200 0x0317 LaserJet 1200 product HP 5200C 0x0401 Scanjet 5200C product HP 830C 0x0404 DeskJet 830C product HP 3400CSE 0x0405 ScanJet 3400cse product HP 6300C 0x0601 Scanjet 6300C product HP 840C 0x0604 DeskJet 840c product HP 2200C 0x0605 ScanJet 2200C product HP 5300C 0x0701 Scanjet 5300C product HP 4400C 0x0705 Scanjet 4400C product HP 4470C 0x0805 Scanjet 4470C product HP 82x0C 0x0b01 Scanjet 82x0C product HP 2300D 0x0b17 Laserjet 2300d product HP 970CSE 0x1004 Deskjet 970Cse product HP 5400C 0x1005 Scanjet 5400C product HP 2215 0x1016 iPAQ 22xx/Jornada 548 product HP 568J 0x1116 Jornada 568 product HP 930C 0x1204 DeskJet 930c product HP3 RTL8188CU 0x1629 RTL8188CU product HP P2000U 0x1801 Inkjet P-2000U product HP HS2300 0x1e1d HS2300 HSDPA (aka MC8775) product HP 640C 0x2004 DeskJet 640c product HP 4670V 0x3005 ScanJet 4670v product HP P1100 0x3102 Photosmart P1100 product HP LD220 0x3524 LD220 POS Display product HP OJ4215 0x3d11 OfficeJet 4215 product HP HN210E 0x811c Ethernet HN210E product HP2 C500 0x6002 PhotoSmart C500 product HP EV2200 0x1b1d ev2200 HSDPA (aka MC5720) product HP HS2300 0x1e1d hs2300 HSDPA (aka MC8775) /* HTC products */ product HTC WINMOBILE 0x00ce HTC USB Sync product HTC PPC6700MODEM 0x00cf PPC6700 Modem product HTC SMARTPHONE 0x0a51 SmartPhone USB Sync product HTC WIZARD 0x0bce HTC Wizard USB Sync product HTC LEGENDSYNC 0x0c97 HTC Legend USB Sync product HTC LEGEND 0x0ff9 HTC Legend product HTC LEGENDINTERNET 0x0ffe HTC Legend Internet Sharing /* HUAWEI products */ product HUAWEI MOBILE 0x1001 Huawei Mobile product HUAWEI E220 0x1003 HSDPA modem product HUAWEI E220BIS 0x1004 HSDPA modem product HUAWEI E1401 0x1401 3G modem product HUAWEI E1402 0x1402 3G modem product HUAWEI E1403 0x1403 3G modem product HUAWEI E1404 0x1404 3G modem product HUAWEI E1405 0x1405 3G modem product HUAWEI E1406 0x1406 3G modem product HUAWEI E1407 0x1407 3G modem product HUAWEI E1408 0x1408 3G modem product HUAWEI E1409 0x1409 3G modem product HUAWEI E140A 0x140a 3G modem product HUAWEI E140B 0x140b 3G modem product HUAWEI E180V 0x140c E180V product HUAWEI E140D 0x140d 3G modem product HUAWEI E140E 0x140e 3G modem product HUAWEI E140F 0x140f 3G modem product HUAWEI E1410 0x1410 3G modem product HUAWEI E1411 0x1411 3G modem product HUAWEI E1412 0x1412 3G modem product HUAWEI E1413 0x1413 3G modem product HUAWEI E1414 0x1414 3G modem product HUAWEI E1415 0x1415 3G modem product HUAWEI E1416 0x1416 3G modem product HUAWEI E1417 0x1417 3G modem product HUAWEI E1418 0x1418 3G modem product HUAWEI E1419 0x1419 3G modem product HUAWEI E141A 0x141a 3G modem product HUAWEI E141B 0x141b 3G modem product HUAWEI E141C 0x141c 3G modem product HUAWEI E141D 0x141d 3G modem product HUAWEI E141E 0x141e 3G modem product HUAWEI E141F 0x141f 3G modem product HUAWEI E1420 0x1420 3G modem product HUAWEI E1421 0x1421 3G modem product HUAWEI E1422 0x1422 3G modem product HUAWEI E1423 0x1423 3G modem product HUAWEI E1424 0x1424 3G modem product HUAWEI E1425 0x1425 3G modem product HUAWEI E1426 0x1426 3G modem product HUAWEI E1427 0x1427 3G modem product HUAWEI E1428 0x1428 3G modem product HUAWEI E1429 0x1429 3G modem product HUAWEI E142A 0x142a 3G modem product HUAWEI E142B 0x142b 3G modem product HUAWEI E142C 0x142c 3G modem product HUAWEI E142D 0x142d 3G modem product HUAWEI E142E 0x142e 3G modem product HUAWEI E142F 0x142f 3G modem product HUAWEI E1430 0x1430 3G modem product HUAWEI E1431 0x1431 3G modem product HUAWEI E1432 0x1432 3G modem product HUAWEI E1433 0x1433 3G modem product HUAWEI E1434 0x1434 3G modem product HUAWEI E1435 0x1435 3G modem product HUAWEI E1436 0x1436 3G modem product HUAWEI E1437 0x1437 3G modem product HUAWEI E1438 0x1438 3G modem product HUAWEI E1439 0x1439 3G modem product HUAWEI E143A 0x143a 3G modem product HUAWEI E143B 0x143b 3G modem product HUAWEI E143C 0x143c 3G modem product HUAWEI E143D 0x143d 3G modem product HUAWEI E143E 0x143e 3G modem product HUAWEI E143F 0x143f 3G modem product HUAWEI E1752 0x1446 3G modem product HUAWEI K4505 0x1464 3G modem product HUAWEI K3765 0x1465 3G modem product HUAWEI E1820 0x14ac E1820 HSPA+ USB Slider product HUAWEI K3771_INIT 0x14c4 K3771 Initial product HUAWEI K3770 0x14c9 3G modem product HUAWEI K3771 0x14ca K3771 product HUAWEI K3772 0x14cf K3772 product HUAWEI K3770_INIT 0x14d1 K3770 Initial product HUAWEI E3131_INIT 0x14fe 3G modem initial product HUAWEI E392 0x1505 LTE modem product HUAWEI E3131 0x1506 3G modem product HUAWEI K3765_INIT 0x1520 K3765 Initial product HUAWEI K4505_INIT 0x1521 K4505 Initial product HUAWEI K3772_INIT 0x1526 K3772 Initial product HUAWEI E3272_INIT 0x155b LTE modem initial product HUAWEI ME909U 0x1573 LTE modem product HUAWEI R215_INIT 0x1582 LTE modem initial product HUAWEI R215 0x1588 LTE modem product HUAWEI ME909S 0x15c1 LTE modem product HUAWEI ETS2055 0x1803 CDMA modem product HUAWEI E173 0x1c05 3G modem product HUAWEI E173_INIT 0x1c0b 3G modem initial product HUAWEI E3272 0x1c1e LTE modem /* HUAWEI 3com products */ product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g /* IBM Corporation */ product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive product IBM USB4543 0x4543 TI IBM USB 4543 Modem product IBM USB454B 0x454b TI IBM USB 454B Modem product IBM USB454C 0x454c TI IBM USB 454C Modem /* Icom products */ product ICOM SP1 0x0004 FTDI compatible adapter product ICOM OPC_U_UC 0x0018 FTDI compatible adapter product ICOM RP2C1 0x0009 FTDI compatible adapter product ICOM RP2C2 0x000a FTDI compatible adapter product ICOM RP2D 0x000b FTDI compatible adapter product ICOM RP2KVR 0x0013 FTDI compatible adapter product ICOM RP2KVT 0x0012 FTDI compatible adapter product ICOM RP2VR 0x000d FTDI compatible adapter product ICOM RP2VT 0x000c FTDI compatible adapter product ICOM RP4KVR 0x0011 FTDI compatible adapter product ICOM RP4KVT 0x0010 FTDI compatible adapter /* ID-tech products */ product IDTECH IDT1221U 0x0300 FTDI compatible adapter /* Imagination Technologies products */ product IMAGINATION DBX1 0x2107 DBX1 DSP core /* Initio Corporation products */ product INITIO DUMMY 0x0000 Dummy product product INITIO INIC_1610P 0x1e40 USB to SATA Bridge /* Inside Out Networks products */ product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports /* In-System products */ product INSYSTEM F5U002 0x0002 Parallel printer product INSYSTEM ATAPI 0x0031 ATAPI Adapter product INSYSTEM ISD110 0x0200 IDE Adapter ISD110 product INSYSTEM ISD105 0x0202 IDE Adapter ISD105 product INSYSTEM USBCABLE 0x081a USB cable product INSYSTEM STORAGE_V2 0x5701 USB Storage Adapter V2 /* Intenso products */ product INTENSO MEMORY_BOX 0x0701 External disk /* Intel products */ product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera product INTEL TESTBOARD 0x9890 82930 test board product INTEL2 IRMH 0x0020 Integrated Rate Matching Hub product INTEL2 IRMH2 0x0024 Integrated Rate Matching Hub product INTEL2 IRMH3 0x8000 Integrated Rate Matching Hub product INTEL2 IRMH4 0x8008 Integrated Rate Matching Hub product INTEL2 SFP 0x0aa7 Sandy Peak (3168) Bluetooth Module product INTEL2 JFP 0x0aaa Jefferson Peak (9460/9560) Bluetooth Module product INTEL2 THP 0x0025 Thunder Peak (9160/9260) Bluetooth Module product INTEL2 HSP 0x0026 Harrison Peak (22560) Bluetooth Module /* Interbiometric products */ product INTERBIOMETRICS IOBOARD 0x1002 FTDI compatible adapter product INTERBIOMETRICS MINI_IOBOARD 0x1006 FTDI compatible adapter /* Intersil products */ product INTERSIL PRISM_GT 0x1000 PrismGT USB 2.0 WLAN product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN /* Interpid Control Systems products */ product INTREPIDCS VALUECAN 0x0601 ValueCAN CAN bus interface product INTREPIDCS NEOVI 0x0701 NeoVI Blue vehicle bus interface /* I/O DATA products */ product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2 product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8 product IODATA USBSSMRW 0x0314 USB-SSMRW SD-card product IODATA USBSDRW 0x031e USB-SDRW SD-card product IODATA USBETT 0x0901 USB ETT product IODATA USBETTX 0x0904 USB ETTX product IODATA USBETTXS 0x0913 USB ETTX product IODATA USBWNB11A 0x0919 USB WN-B11 product IODATA USBWNB11 0x0922 USB Airport WN-B11 product IODATA ETGUS2 0x0930 ETG-US2 product IODATA WNGDNUS2 0x093f WN-GDN/US2 product IODATA RT3072_1 0x0944 RT3072 product IODATA RT3072_2 0x0945 RT3072 product IODATA RT3072_3 0x0947 RT3072 product IODATA RT3072_4 0x0948 RT3072 product IODATA WNAC867U 0x0952 WN-AC867U product IODATA USBRSAQ 0x0a03 Serial USB-RSAQ1 product IODATA USBRSAQ5 0x0a0e Serial USB-RSAQ5 product IODATA2 USB2SC 0x0a09 USB2.0-SCSI Bridge USB2-SC /* Iomega products */ product IOMEGA ZIP100 0x0001 Zip 100 product IOMEGA ZIP250 0x0030 Zip 250 /* Ionic products */ product IONICS PLUGCOMPUTER 0x0102 FTDI compatible adapter /* Integrated System Solution Corp. products */ product ISSC ISSCBTA 0x1001 Bluetooth USB Adapter /* iTegno products */ product ITEGNO WM1080A 0x1080 WM1080A GSM/GPRS modem product ITEGNO WM2080A 0x2080 WM2080A CDMA modem /* Ituner networks products */ product ITUNERNET USBLCD2X20 0x0002 USB-LCD 2x20 product ITUNERNET USBLCD4X20 0xc001 USB-LCD 4x20 /* Jablotron products */ product JABLOTRON PC60B 0x0001 PC-60B /* Jaton products */ product JATON EDA 0x5704 Ethernet /* Jeti products */ product JETI SPC1201 0x04b2 FTDI compatible adapter /* JMicron products */ product JMICRON JMS567 0x0567 USB to SATA 6.0Gb/s bridge product JMICRON JM20336 0x2336 USB to SATA Bridge product JMICRON JM20337 0x2338 USB to ATA/ATAPI Bridge /* JVC products */ product JVC GR_DX95 0x000a GR-DX95 product JVC MP_PRX1 0x3008 MP-PRX1 Ethernet /* JRC products */ product JRC AH_J3001V_J3002V 0x0001 AirH PHONE AH-J3001V/J3002V /* Kamstrrup products */ product KAMSTRUP OPTICALEYE 0x0001 Optical Eye/3-wire product KAMSTRUP MBUS_250D 0x0005 M-Bus Master MultiPort 250D /* Kawatsu products */ product KAWATSU MH4000P 0x0003 MiniHub 4000P /* Keisokugiken Corp. products */ product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ /* Kensington products */ product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball product KENSINGTON TURBOBALL 0x1005 TurboBall /* Keyspan products */ product KEYSPAN USA28_NF 0x0101 USA-28 serial Adapter (no firmware) product KEYSPAN USA28X_NF 0x0102 USA-28X serial Adapter (no firmware) product KEYSPAN USA19_NF 0x0103 USA-19 serial Adapter (no firmware) product KEYSPAN USA18_NF 0x0104 USA-18 serial Adapter (no firmware) product KEYSPAN USA18X_NF 0x0105 USA-18X serial Adapter (no firmware) product KEYSPAN USA19W_NF 0x0106 USA-19W serial Adapter (no firmware) product KEYSPAN USA19 0x0107 USA-19 serial Adapter product KEYSPAN USA19W 0x0108 USA-19W serial Adapter product KEYSPAN USA49W_NF 0x0109 USA-49W serial Adapter (no firmware) product KEYSPAN USA49W 0x010a USA-49W serial Adapter product KEYSPAN USA19QI_NF 0x010b USA-19QI serial Adapter (no firmware) product KEYSPAN USA19QI 0x010c USA-19QI serial Adapter product KEYSPAN USA19Q_NF 0x010d USA-19Q serial Adapter (no firmware) product KEYSPAN USA19Q 0x010e USA-19Q serial Adapter product KEYSPAN USA28 0x010f USA-28 serial Adapter product KEYSPAN USA28XXB 0x0110 USA-28X/XB serial Adapter product KEYSPAN USA18 0x0111 USA-18 serial Adapter product KEYSPAN USA18X 0x0112 USA-18X serial Adapter product KEYSPAN USA28XB_NF 0x0113 USA-28XB serial Adapter (no firmware) product KEYSPAN USA28XA_NF 0x0114 USA-28XB serial Adapter (no firmware) product KEYSPAN USA28XA 0x0115 USA-28XA serial Adapter product KEYSPAN USA18XA_NF 0x0116 USA-18XA serial Adapter (no firmware) product KEYSPAN USA18XA 0x0117 USA-18XA serial Adapter product KEYSPAN USA19QW_NF 0x0118 USA-19WQ serial Adapter (no firmware) product KEYSPAN USA19QW 0x0119 USA-19WQ serial Adapter product KEYSPAN USA19HA 0x0121 USA-19HS serial Adapter product KEYSPAN UIA10 0x0201 UIA-10 remote control product KEYSPAN UIA11 0x0202 UIA-11 remote control /* Kingston products */ product KINGSTON XX1 0x0008 Ethernet product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet product KINGSTON HYPERX3_0 0x162b DT HyperX 3.0 /* Kawasaki products */ product KLSI DUH3E10BT 0x0008 USB Ethernet product KLSI DUH3E10BTN 0x0009 USB Ethernet /* Kobil products */ product KOBIL CONV_B1 0x2020 FTDI compatible adapter product KOBIL CONV_KAAN 0x2021 FTDI compatible adapter /* Kodak products */ product KODAK DC220 0x0100 Digital Science DC220 product KODAK DC260 0x0110 Digital Science DC260 product KODAK DC265 0x0111 Digital Science DC265 product KODAK DC290 0x0112 Digital Science DC290 product KODAK DC240 0x0120 Digital Science DC240 product KODAK DC280 0x0130 Digital Science DC280 /* Kontron AG products */ product KONTRON DM9601 0x8101 USB Ethernet product KONTRON JP1082 0x9700 USB Ethernet /* Konica Corp. Products */ product KONICA CAMERA 0x0720 Digital Color Camera /* KYE products */ product KYE NICHE 0x0001 Niche mouse product KYE NETSCROLL 0x0003 Genius NetScroll mouse product KYE FLIGHT2000 0x1004 Flight 2000 joystick product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner /* Kyocera products */ product KYOCERA FINECAM_S3X 0x0100 Finecam S3x product KYOCERA FINECAM_S4 0x0101 Finecam S4 product KYOCERA FINECAM_S5 0x0103 Finecam S5 product KYOCERA FINECAM_L3 0x0105 Finecam L3 product KYOCERA AHK3001V 0x0203 AH-K3001V product KYOCERA2 CDMA_MSM_K 0x17da Qualcomm Kyocera CDMA Technologies MSM product KYOCERA2 KPC680 0x180a Qualcomm Kyocera CDMA Technologies MSM /* LaCie products */ product LACIE HD 0xa601 Hard Disk product LACIE CDRW 0xa602 CD R/W /* Lake Shore Cryotronics products */ product LAKESHORE 121 0x0100 121 Current Source product LAKESHORE 218A 0x0200 218A Temperature Monitor product LAKESHORE 219 0x0201 219 Temperature Monitor product LAKESHORE 233 0x0202 233 Temperature Transmitter product LAKESHORE 235 0x0203 235 Temperature Transmitter product LAKESHORE 335 0x0300 335 Temperature Controller product LAKESHORE 336 0x0301 336 Temperature Controller product LAKESHORE 350 0x0302 350 Temperature Controller product LAKESHORE 371 0x0303 371 AC Bridge product LAKESHORE 411 0x0400 411 Handheld Gaussmeter product LAKESHORE 425 0x0401 425 Gaussmeter product LAKESHORE 455A 0x0402 455A DSP Gaussmeter product LAKESHORE 475A 0x0403 475A DSP Gaussmeter product LAKESHORE 465 0x0404 465 Gaussmeter product LAKESHORE 625A 0x0600 625A Magnet PSU product LAKESHORE 642A 0x0601 642A Magnet PSU product LAKESHORE 648 0x0602 648 Magnet PSU product LAKESHORE 737 0x0700 737 VSM Controller product LAKESHORE 776 0x0701 776 Matrix Switch /* Larsen and Brusgaard products */ product LARSENBRUSGAARD ALTITRACK 0x0001 FTDI compatible adapter /* Leadtek products */ product LEADTEK 9531 0x2101 9531 GPS /* Lenovo products */ product LENOVO GIGALAN 0x304b USB 3.0 Ethernet product LENOVO ETHERNET 0x7203 USB 2.0 Ethernet product LENOVO RTL8153 0x7205 USB 3.0 Ethernet product LENOVO TBT3LAN 0x3069 LAN port in Thinkpad TB3 dock product LENOVO USBCLAN 0x3062 LAN port in Thinkpad USB-C dock /* Lexar products */ product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader product LEXAR CF_READER 0xb002 USB CF Reader product LEXAR JUMPDRIVE 0xa833 USB Jumpdrive Flash Drive /* Lexmark products */ product LEXMARK S2450 0x0009 Optra S 2450 /* Liebert products */ product LIEBERT POWERSURE_PXT 0xffff PowerSure Personal XT product LIEBERT2 PSI1000 0x0004 UPS PSI 1000 FW:08 /* Link Instruments Inc. products */ product LINKINSTRUMENTS MSO19 0xf190 Link Instruments MSO-19 product LINKINSTRUMENTS MSO28 0xf280 Link Instruments MSO-28 product LINKINSTRUMENTS MSO28_2 0xf281 Link Instruments MSO-28 /* Linksys products */ product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2 product LINKSYS USB10TX1 0x200c USB10TX product LINKSYS USB10T 0x2202 USB10T Ethernet product LINKSYS USB100TX 0x2203 USB100TX Ethernet product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA product LINKSYS USB10TA 0x2206 USB10TA Ethernet product LINKSYS USB10TX2 0x400b USB10TX product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless Adapter product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 Ethernet product LINKSYS3 WUSB11v28 0x2233 WUSB11 v2.8 Wireless Adapter product LINKSYS4 USB1000 0x0039 USB1000 product LINKSYS4 WUSB100 0x0070 WUSB100 product LINKSYS4 WUSB600N 0x0071 WUSB600N product LINKSYS4 WUSB54GCV2 0x0073 WUSB54GC v2 product LINKSYS4 WUSB54GCV3 0x0077 WUSB54GC v3 product LINKSYS4 RT3070 0x0078 RT3070 product LINKSYS4 WUSB600NV2 0x0079 WUSB600N v2 /* Logilink products */ product LOGILINK DUMMY 0x0000 Dummy product product LOGILINK U2M 0x0101 LogiLink USB MIDI Cable /* Logitech products */ product LOGITECH M2452 0x0203 M2452 keyboard product LOGITECH M4848 0x0301 M4848 mouse product LOGITECH PAGESCAN 0x040f PageScan product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro product LOGITECH WEBCAMC100 0X0817 Webcam C100 product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express product LOGITECH QUICKCAM 0x0850 QuickCam product LOGITECH QUICKCAMPRO3 0x0990 QuickCam Pro 9000 product LOGITECH N43 0xc000 N43 product LOGITECH N48 0xc001 N48 mouse product LOGITECH MBA47 0xc002 M-BA47 mouse product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse product LOGITECH BD58 0xc00c BD58 mouse product LOGITECH UN58A 0xc030 iFeel Mouse product LOGITECH UN53B 0xc032 iFeel MouseMan product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme product LOGITECH WMRPAD 0xc20a WingMan RumblePad product LOGITECH WMJOY 0xc281 WingMan Force joystick product LOGITECH BB13 0xc401 USB-PS/2 Trackball product LOGITECH RK53 0xc501 Cordless mouse product LOGITECH RB6 0xc503 Cordless keyboard product LOGITECH MX700 0xc506 Cordless optical mouse product LOGITECH UNIFYING 0xc52b Logitech Unifying Receiver product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro /* Logitec Corp. products */ product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2 product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2 product LOGITEC LAN_GTJU2A 0x0160 LAN-GTJ/U2A Ethernet product LOGITEC RT2870_1 0x0162 RT2870 product LOGITEC RT2870_2 0x0163 RT2870 product LOGITEC RT2870_3 0x0164 RT2870 product LOGITEC LANW300NU2 0x0166 LAN-W300N/U2 product LOGITEC LANW150NU2 0x0168 LAN-W150N/U2 product LOGITEC LANW300NU2S 0x0169 LAN-W300N/U2S /* Longcheer Holdings, Ltd. products */ product LONGCHEER WM66 0x6061 Longcheer WM66 HSDPA product LONGCHEER W14 0x9603 Mobilcom W14 product LONGCHEER DISK 0xf000 Driver disk product LONGCHEER XSSTICK 0x9605 4G Systems XSStick P14 /* Lucent products */ product LUCENT EVALKIT 0x1001 USS-720 evaluation kit /* Luwen products */ product LUWEN EASYDISK 0x0005 EasyDisc /* Macally products */ product MACALLY MOUSE1 0x0101 mouse /* Mag-Tek products */ product MAGTEK USBSWIPE 0x0002 USB Mag Stripe Swipe Reader /* Marvell Technology Group, Ltd. products */ product MARVELL SHEEVAPLUG 0x9e8f SheevaPlug serial interface /* Matrix Orbital products */ product MATRIXORBITAL FTDI_RANGE_0100 0x0100 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0101 0x0101 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0102 0x0102 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0103 0x0103 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0104 0x0104 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0105 0x0105 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0106 0x0106 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0107 0x0107 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0108 0x0108 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0109 0x0109 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010A 0x010a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010B 0x010b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010C 0x010c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010D 0x010d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010E 0x010e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010F 0x010f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0110 0x0110 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0111 0x0111 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0112 0x0112 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0113 0x0113 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0114 0x0114 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0115 0x0115 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0116 0x0116 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0117 0x0117 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0118 0x0118 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0119 0x0119 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011A 0x011a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011B 0x011b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011C 0x011c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011D 0x011d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011E 0x011e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011F 0x011f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0120 0x0120 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0121 0x0121 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0122 0x0122 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0123 0x0123 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0124 0x0124 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0125 0x0125 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0126 0x0126 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0128 0x0128 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0129 0x0129 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012A 0x012a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012B 0x012b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012D 0x012d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012E 0x012e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012F 0x012f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0130 0x0130 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0131 0x0131 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0132 0x0132 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0133 0x0133 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0134 0x0134 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0135 0x0135 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0136 0x0136 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0137 0x0137 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0138 0x0138 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0139 0x0139 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013A 0x013a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013B 0x013b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013C 0x013c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013D 0x013d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013E 0x013e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013F 0x013f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0140 0x0140 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0141 0x0141 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0142 0x0142 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0143 0x0143 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0144 0x0144 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0145 0x0145 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0146 0x0146 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0147 0x0147 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0148 0x0148 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0149 0x0149 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014A 0x014a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014B 0x014b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014C 0x014c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014D 0x014d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014E 0x014e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014F 0x014f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0150 0x0150 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0151 0x0151 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0152 0x0152 FTDI compatible adapter product MATRIXORBITAL MOUA 0x0153 Martrix Orbital MOU-Axxxx LCD displays product MATRIXORBITAL FTDI_RANGE_0159 0x0159 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015A 0x015a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015B 0x015b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015C 0x015c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015D 0x015d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015E 0x015e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015F 0x015f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0160 0x0160 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0161 0x0161 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0162 0x0162 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0163 0x0163 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0164 0x0164 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0165 0x0165 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0166 0x0166 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0167 0x0167 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0168 0x0168 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0169 0x0169 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016A 0x016a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016B 0x016b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016C 0x016c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016D 0x016d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016E 0x016e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016F 0x016f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0170 0x0170 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0171 0x0171 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0172 0x0172 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0173 0x0173 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0174 0x0174 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0175 0x0175 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0176 0x0176 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0177 0x0177 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0178 0x0178 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0179 0x0179 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017A 0x017a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017B 0x017b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017C 0x017c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017D 0x017d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017E 0x017e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017F 0x017f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0180 0x0180 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0181 0x0181 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0182 0x0182 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0183 0x0183 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0184 0x0184 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0185 0x0185 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0186 0x0186 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0187 0x0187 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0188 0x0188 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0189 0x0189 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018A 0x018a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018B 0x018b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018C 0x018c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018D 0x018d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018E 0x018e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018F 0x018f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0190 0x0190 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0191 0x0191 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0192 0x0192 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0193 0x0193 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0194 0x0194 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0195 0x0195 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0196 0x0196 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0197 0x0197 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0198 0x0198 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0199 0x0199 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019A 0x019a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019B 0x019b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019C 0x019c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019D 0x019d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019E 0x019e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019F 0x019f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A0 0x01a0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A1 0x01a1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A2 0x01a2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A3 0x01a3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A4 0x01a4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A5 0x01a5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A6 0x01a6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A7 0x01a7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A8 0x01a8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A9 0x01a9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AA 0x01aa FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AB 0x01ab FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AC 0x01ac FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AD 0x01ad FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AE 0x01ae FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AF 0x01af FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B0 0x01b0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B1 0x01b1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B2 0x01b2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B3 0x01b3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B4 0x01b4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B5 0x01b5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B6 0x01b6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B7 0x01b7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B8 0x01b8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B9 0x01b9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BA 0x01ba FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BB 0x01bb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BC 0x01bc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BD 0x01bd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BE 0x01be FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BF 0x01bf FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C0 0x01c0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C1 0x01c1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C2 0x01c2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C3 0x01c3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C4 0x01c4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C5 0x01c5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C6 0x01c6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C7 0x01c7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C8 0x01c8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C9 0x01c9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CA 0x01ca FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CB 0x01cb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CC 0x01cc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CD 0x01cd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CE 0x01ce FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CF 0x01cf FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D0 0x01d0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D1 0x01d1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D2 0x01d2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D3 0x01d3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D4 0x01d4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D5 0x01d5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D6 0x01d6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D7 0x01d7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D8 0x01d8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D9 0x01d9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DA 0x01da FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DB 0x01db FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DC 0x01dc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DD 0x01dd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DE 0x01de FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DF 0x01df FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E0 0x01e0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E1 0x01e1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E2 0x01e2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E3 0x01e3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E4 0x01e4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E5 0x01e5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E6 0x01e6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E7 0x01e7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E8 0x01e8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E9 0x01e9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EA 0x01ea FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EB 0x01eb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EC 0x01ec FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01ED 0x01ed FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EE 0x01ee FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EF 0x01ef FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F0 0x01f0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F1 0x01f1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F2 0x01f2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F3 0x01f3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F4 0x01f4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F5 0x01f5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F6 0x01f6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F7 0x01f7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F8 0x01f8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F9 0x01f9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FA 0x01fa FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FB 0x01fb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FC 0x01fc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FD 0x01fd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FE 0x01fe FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FF 0x01ff FTDI compatible adapter /* MCT Corp. */ product MCT HUB0100 0x0100 Hub product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub product MCT USB232 0x0210 USB-232 Interface product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products /* Medeli */ product MEDELI DD305 0x5011 DD305 Digital Drum Set /* MediaTek, Inc. */ product MEDIATEK MTK3329 0x3329 MTK II GPS Receiver /* Meizu Electronics */ product MEIZU M6_SL 0x0140 MiniPlayer M6 (SL) /* Melco, Inc products */ product MELCO LUATX1 0x0001 LUA-TX Ethernet product MELCO LUATX5 0x0005 LUA-TX Ethernet product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet product MELCO LUAKTX 0x0012 LUA-KTX Ethernet product MELCO DUBPXXG 0x001c DUB-PxxG product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet product MELCO KG54YB 0x005e WLI-U2-KG54-YB WLAN product MELCO KG54 0x0066 WLI-U2-KG54 WLAN product MELCO KG54AI 0x0067 WLI-U2-KG54-AI WLAN product MELCO LUA3U2AGT 0x006e LUA3-U2-AGT product MELCO NINWIFI 0x008b Nintendo Wi-Fi product MELCO PCOPRS1 0x00b3 PC-OP-RS1 RemoteStation product MELCO SG54HP 0x00d8 WLI-U2-SG54HP product MELCO G54HP 0x00d9 WLI-U2-G54HP product MELCO KG54L 0x00da WLI-U2-KG54L product MELCO WLIUCG300N 0x00e8 WLI-UC-G300N product MELCO SG54HG 0x00f4 WLI-U2-SG54HG product MELCO WLRUCG 0x0116 WLR-UC-G product MELCO WLRUCGAOSS 0x0119 WLR-UC-G-AOSS product MELCO WLIUCAG300N 0x012e WLI-UC-AG300N product MELCO WLIUCG 0x0137 WLI-UC-G product MELCO WLIUCG300HP 0x0148 WLI-UC-G300HP product MELCO RT2870_2 0x0150 RT2870 product MELCO WLIUCGN 0x015d WLI-UC-GN product MELCO WLIUCG301N 0x016f WLI-UC-G301N product MELCO WLIUCGNM 0x01a2 WLI-UC-GNM product MELCO WLIUCG300HPV1 0x01a8 WLI-UC-G300HP-V1 product MELCO WLIUCGNM2 0x01ee WLI-UC-GNM2 product MELCO WIU2433DM 0x0242 WI-U2-433DM product MELCO WIU3866D 0x025d WI-U3-866D /* Merlin products */ product MERLIN V620 0x1110 Merlin V620 /* MetaGeek products */ product METAGEEK TELLSTICK 0x0c30 FTDI compatible adapter product METAGEEK WISPY1B 0x083e MetaGeek Wi-Spy product METAGEEK WISPY24X 0x083f MetaGeek Wi-Spy 2.4x product METAGEEK2 WISPYDBX 0x5000 MetaGeek Wi-Spy DBx /* Metricom products */ product METRICOM RICOCHET_GS 0x0001 Ricochet GS /* MGE UPS Systems */ product MGE UPS1 0x0001 MGE UPS SYSTEMS PROTECTIONCENTER 1 product MGE UPS2 0xffff MGE UPS SYSTEMS PROTECTIONCENTER 2 /* MEI products */ product MEI CASHFLOW_SC 0x1100 Cashflow-SC Cash Acceptor product MEI S2000 0x1101 Series 2000 Combo Acceptor /* Microdia / Sonix Techonology Co., Ltd. products */ product CHICONY2 YUREX 0x1010 YUREX product CHICONY2 CAM_1 0x62c0 CAM_1 product CHICONY2 TEMPER 0x7401 TEMPer sensor /* Micro Star International products */ product MSI BT_DONGLE 0x1967 Bluetooth USB dongle product MSI RT3070_1 0x3820 RT3070 product MSI RT3070_2 0x3821 RT3070 product MSI RT3070_8 0x3822 RT3070 product MSI RT3070_3 0x3870 RT3070 product MSI RT3070_9 0x3871 RT3070 product MSI UB11B 0x6823 UB11B product MSI RT2570 0x6861 RT2570 product MSI RT2570_2 0x6865 RT2570 product MSI RT2570_3 0x6869 RT2570 product MSI RT2573_1 0x6874 RT2573 product MSI RT2573_2 0x6877 RT2573 product MSI RT3070_4 0x6899 RT3070 product MSI RT3070_5 0x821a RT3070 product MSI RT3070_10 0x822a RT3070 product MSI RT3070_6 0x870a RT3070 product MSI RT3070_11 0x871a RT3070 product MSI RT3070_7 0x899a RT3070 product MSI RT2573_3 0xa861 RT2573 product MSI RT2573_4 0xa874 RT2573 /* Micron products */ product MICRON REALSSD 0x0655 Real SSD eUSB /* Microsoft products */ product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite product MICROSOFT DDS80 0x0014 Digital Sound System 80 product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel product MICROSOFT INETPRO 0x001c Internet Keyboard Pro product MICROSOFT TBEXPLORER 0x0024 Trackball Explorer product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro product MICROSOFT INTELLIMOUSE5 0x0039 IntelliMouse 1.1 5-Button Mouse product MICROSOFT WHEELMOUSE 0x0040 Wheel Mouse Optical product MICROSOFT MN510 0x006e MN510 Wireless product MICROSOFT 700WX 0x0079 Palm 700WX product MICROSOFT MN110 0x007a 10/100 USB NIC product MICROSOFT WLINTELLIMOUSE 0x008c Wireless Optical IntelliMouse product MICROSOFT WLNOTEBOOK 0x00b9 Wireless Optical Mouse (Model 1023) product MICROSOFT COMFORT3000 0x00d1 Comfort Optical Mouse 3000 (Model 1043) product MICROSOFT WLNOTEBOOK3 0x00d2 Wireless Optical Mouse 3000 (Model 1049) product MICROSOFT NATURAL4000 0x00db Natural Ergonomic Keyboard 4000 product MICROSOFT WLNOTEBOOK2 0x00e1 Wireless Optical Mouse 3000 (Model 1056) product MICROSOFT XBOX360 0x0292 XBOX 360 WLAN /* Microtech products */ product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25 product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50 product MICROTECH DPCM 0x0006 USB CameraMate product MICROTECH FREECOM 0xfc01 Freecom USB-IDE /* Microtek products */ product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner product MICROTEK X6U 0x0099 ScanMaker X6 - X6U product MICROTEK C6 0x009a Phantom C6 scanner product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner product MICROTEK V6USL 0x00a3 ScanMaker V6USL product MICROTEK V6USL2 0x80a3 ScanMaker V6USL product MICROTEK V6UL 0x80ac ScanMaker V6UL /* Microtune, Inc. products */ product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle /* Midiman products */ product MAUDIO MIDISPORT2X2 0x1001 Midisport 2x2 product MAUDIO FASTTRACKULTRA 0x2080 Fast Track Ultra product MAUDIO FASTTRACKULTRA8R 0x2081 Fast Track Ultra 8R /* MindsAtWork products */ product MINDSATWORK WALLET 0x0001 Digital Wallet /* Minolta Co., Ltd. */ product MINOLTA 2300 0x4001 Dimage 2300 product MINOLTA S304 0x4007 Dimage S304 product MINOLTA X 0x4009 Dimage X product MINOLTA 5400 0x400e Dimage 5400 product MINOLTA F300 0x4011 Dimage F300 product MINOLTA E223 0x4017 Dimage E223 /* Mitsumi products */ product MITSUMI CDRRW 0x0000 CD-R/RW Drive product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle product MITSUMI FDD 0x6901 USB FDD /* Mobile Action products */ product MOBILEACTION MA620 0x0620 MA-620 Infrared Adapter /* Mobility products */ product MOBILITY USB_SERIAL 0x0202 FTDI compatible adapter product MOBILITY EA 0x0204 Ethernet product MOBILITY EASIDOCK 0x0304 EasiDock Ethernet /* MosChip products */ product MOSCHIP MCS7703 0x7703 MCS7703 Serial Port Adapter product MOSCHIP MCS7730 0x7730 MCS7730 Ethernet product MOSCHIP MCS7820 0x7820 MCS7820 Serial Port Adapter product MOSCHIP MCS7830 0x7830 MCS7830 Ethernet product MOSCHIP MCS7832 0x7832 MCS7832 Ethernet product MOSCHIP MCS7840 0x7840 MCS7840 Serial Port Adapter /* Motorola products */ product MOTOROLA MC141555 0x1555 MC141555 hub controller product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem product MOTOROLA2 T720C 0x2822 T720c product MOTOROLA2 A41XV32X 0x2a22 A41x/V32x Mobile Phones product MOTOROLA2 E398 0x4810 E398 Mobile Phone product MOTOROLA2 USBLAN 0x600c USBLAN product MOTOROLA2 USBLAN2 0x6027 USBLAN product MOTOROLA2 MB886 0x710f MB886 Mobile Phone (Atria HD) product MOTOROLA4 RT2770 0x9031 RT2770 product MOTOROLA4 RT3070 0x9032 RT3070 /* Moxa */ product MOXA MXU1_1110 0x1110 Moxa Uport 1110 product MOXA MXU1_1130 0x1130 Moxa Uport 1130 product MOXA MXU1_1131 0x1131 Moxa Uport 1131 product MOXA MXU1_1150 0x1150 Moxa Uport 1150 product MOXA MXU1_1151 0x1151 Moxa Uport 1151 /* MpMan products */ product MPMAN MPF400_2 0x25a8 MPF400 Music Player 2Go product MPMAN MPF400_1 0x36d0 MPF400 Music Player 1Go /* MultiTech products */ product MULTITECH MT9234ZBA_2 0x0319 MT9234ZBA USB modem (alt) product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem product MULTITECH GSM 0xf108 GSM USB Modem product MULTITECH CDMA 0xf109 CDMA USB Modem product MULTITECH CDMA_FW 0xf110 CDMA USB Modem firmware running product MULTITECH GSM_FW 0xf111 GSM USB Modem firmware running product MULTITECH EDGE 0xf112 Edge USB Modem product MULTITECH MT9234MU 0xf114 MT9234 MU product MULTITECH MT9234ZBA 0xf115 MT9234 ZBA /* Mustek products */ product MUSTEK 1200CU 0x0001 1200 CU scanner product MUSTEK 600CU 0x0002 600 CU scanner product MUSTEK 1200USB 0x0003 1200 USB scanner product MUSTEK 1200UB 0x0006 1200 UB scanner product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner product MUSTEK BEARPAW2400TA 0x0218 BearPaw 2400TA scanner product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner product MUSTEK 600USB 0x0873 600 USB scanner product MUSTEK MDC800 0xa800 MDC-800 digital camera /* M-Systems products */ product MSYSTEMS DISKONKEY 0x0010 DiskOnKey product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey /* Myson products */ product MYSON HEDEN_8813 0x8813 USB-IDE product MYSON HEDEN 0x8818 USB-IDE product MYSON HUBREADER 0x8819 COMBO Card reader with USB HUB product MYSON STARREADER 0x9920 USB flash card adapter /* National Semiconductor */ product NATIONAL BEARPAW1200 0x1000 BearPaw 1200 product NATIONAL BEARPAW2400 0x1001 BearPaw 2400 /* NEC products */ product NEC HUB_0050 0x0050 USB 2.0 7-Port Hub product NEC HUB_005A 0x005a USB 2.0 4-Port Hub product NEC WL300NUG 0x0249 WL300NU-G product NEC WL900U 0x0408 Aterm WL900U product NEC HUB 0x55aa hub product NEC HUB_B 0x55ab hub /* NEODIO products */ product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller product NEODIO ND5010 0x5010 Multi-format Flash Controller /* Neotel products */ product NEOTEL PRIME 0x4000 Prime USB modem /* Netac products */ product NETAC CF_CARD 0x1060 USB-CF-Card product NETAC ONLYDISK 0x0003 OnlyDisk /* NetChip Technology Products */ product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect product NETCHIP CLIK_40 0xa140 USB Clik! 40 product NETCHIP GADGETZERO 0xa4a0 Linux Gadget Zero product NETCHIP ETHERNETGADGET 0xa4a2 Linux Ethernet/RNDIS gadget on pxa210/25x/26x product NETCHIP POCKETBOOK 0xa4a5 PocketBook /* Netgear products */ product NETGEAR EA101 0x1001 Ethernet product NETGEAR EA101X 0x1002 Ethernet product NETGEAR FA101 0x1020 Ethernet 10/100, USB1.1 product NETGEAR FA120 0x1040 USB 2.0 Ethernet product NETGEAR M4100 0x1100 M4100/M5300/M7100 series switch product NETGEAR WG111V1_2 0x4240 PrismGT USB 2.0 WLAN product NETGEAR WG111V3 0x4260 WG111v3 product NETGEAR WG111U 0x4300 WG111U product NETGEAR WG111U_NF 0x4301 WG111U (no firmware) product NETGEAR WG111V2 0x6a00 WG111V2 product NETGEAR WN111V2 0x9001 WN111V2 product NETGEAR WNDA3100 0x9010 WNDA3100 product NETGEAR WNDA4100 0x9012 WNDA4100 product NETGEAR WNDA3200 0x9018 WNDA3200 product NETGEAR RTL8192CU 0x9021 RTL8192CU product NETGEAR WNA1000 0x9040 WNA1000 product NETGEAR WNA1000M 0x9041 WNA1000M product NETGEAR A6100 0x9052 A6100 product NETGEAR2 MA101 0x4100 MA101 product NETGEAR2 MA101B 0x4102 MA101 Rev B product NETGEAR3 WG111T 0x4250 WG111T product NETGEAR3 WG111T_NF 0x4251 WG111T (no firmware) product NETGEAR3 WPN111 0x5f00 WPN111 product NETGEAR3 WPN111_NF 0x5f01 WPN111 (no firmware) product NETGEAR3 WPN111_2 0x5f02 WPN111 product NETGEAR4 RTL8188CU 0x9041 RTL8188CU /* NetIndex products */ product NETINDEX WS002IN 0x2001 Willcom WS002IN /* NEWlink */ product NEWLINK USB2IDEBRIDGE 0x00ff USB 2.0 Hard Drive Enclosure /* Nikon products */ product NIKON E990 0x0102 Digital Camera E990 product NIKON LS40 0x4000 CoolScan LS40 ED product NIKON D300 0x041a Digital Camera D300 /* NovaTech Products */ product NOVATECH NV902 0x9020 NovaTech NV-902W product NOVATECH RT2573 0x9021 RT2573 product NOVATECH RTL8188CU 0x9071 RTL8188CU /* Nokia products */ product NOKIA N958GB 0x0070 Nokia N95 8GBc product NOKIA2 CA42 0x1234 CA-42 cable /* Novatel Wireless products */ product NOVATEL V640 0x1100 Merlin V620 product NOVATEL CDMA_MODEM 0x1110 Novatel Wireless Merlin CDMA product NOVATEL V620 0x1110 Merlin V620 product NOVATEL V740 0x1120 Merlin V740 product NOVATEL V720 0x1130 Merlin V720 product NOVATEL U740 0x1400 Merlin U740 product NOVATEL U740_2 0x1410 Merlin U740 product NOVATEL U870 0x1420 Merlin U870 product NOVATEL XU870 0x1430 Merlin XU870 product NOVATEL X950D 0x1450 Merlin X950D product NOVATEL ES620 0x2100 Expedite ES620 product NOVATEL E725 0x2120 Expedite E725 product NOVATEL ES620_2 0x2130 Expedite ES620 product NOVATEL ES620 0x2100 ES620 CDMA product NOVATEL U720 0x2110 Merlin U720 product NOVATEL EU730 0x2400 Expedite EU730 product NOVATEL EU740 0x2410 Expedite EU740 product NOVATEL EU870D 0x2420 Expedite EU870D product NOVATEL U727 0x4100 Merlin U727 CDMA product NOVATEL MC950D 0x4400 Novatel MC950D HSUPA product NOVATEL MC990D 0x7001 Novatel MC990D product NOVATEL ZEROCD 0x5010 Novatel ZeroCD product NOVATEL MIFI2200V 0x5020 Novatel MiFi 2200 CDMA Virgin Mobile product NOVATEL ZEROCD2 0x5030 Novatel ZeroCD product NOVATEL MIFI2200 0x5041 Novatel MiFi 2200 CDMA product NOVATEL U727_2 0x5100 Merlin U727 CDMA product NOVATEL U760 0x6000 Novatel U760 product NOVATEL MC760 0x6002 Novatel MC760 product NOVATEL MC547 0x7042 Novatel MC547 product NOVATEL MC679 0x7031 Novatel MC679 product NOVATEL2 FLEXPACKGPS 0x0100 NovAtel FlexPack GPS receiver /* NVIDIA products */ product NVIDIA RTL8153 0x09ff USB 3.0 Ethernet /* Merlin products */ product MERLIN V620 0x1110 Merlin V620 /* O2Micro products */ product O2MICRO OZ776_HUB 0x7761 OZ776 hub product O2MICRO OZ776_CCID_SC 0x7772 OZ776 CCID SC Reader /* Olimex products */ product OLIMEX ARM_USB_OCD 0x0003 FTDI compatible adapter product OLIMEX ARM_USB_OCD_H 0x002b FTDI compatible adapter /* Olympus products */ product OLYMPUS C1 0x0102 C-1 Digital Camera product OLYMPUS C700 0x0105 C-700 Ultra Zoom /* OmniVision Technologies, Inc. products */ product OMNIVISION OV511 0x0511 OV511 Camera product OMNIVISION OV511PLUS 0xa511 OV511+ Camera /* OnSpec Electronic, Inc. */ product ONSPEC SDS_HOTFIND_D 0x0400 SDS-infrared.com Hotfind-D Infrared Camera product ONSPEC MDCFE_B_CF_READER 0xa000 MDCFE-B USB CF Reader product ONSPEC CFMS_RW 0xa001 SIIG/Datafab Memory Stick+CF Reader/Writer product ONSPEC READER 0xa003 Datafab-based Reader product ONSPEC CFSM_READER 0xa005 PNY/Datafab CF+SM Reader product ONSPEC CFSM_READER2 0xa006 Simple Tech/Datafab CF+SM Reader product ONSPEC MDSM_B_READER 0xa103 MDSM-B reader product ONSPEC CFSM_COMBO 0xa109 USB to CF + SM Combo (LC1) product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader product ONSPEC2 IMAGEMATE_SDDR55 0xa103 ImageMate SDDR55 /* Option products */ product OPTION VODAFONEMC3G 0x5000 Vodafone Mobile Connect 3G datacard product OPTION GT3G 0x6000 GlobeTrotter 3G datacard product OPTION GT3GQUAD 0x6300 GlobeTrotter 3G QUAD datacard product OPTION GT3GPLUS 0x6600 GlobeTrotter 3G+ datacard product OPTION GTICON322 0xd033 GlobeTrotter Icon322 storage product OPTION GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem product OPTION GTMAX72 0x6711 GlobeTrotter Max 7.2 HSDPA product OPTION GTHSDPA 0x6971 GlobeTrotter HSDPA product OPTION GTMAXHSUPA 0x7001 GlobeTrotter HSUPA product OPTION GTMAXHSUPAE 0x6901 GlobeTrotter HSUPA PCIe product OPTION GTMAX380HSUPAE 0x7211 GlobeTrotter 380HSUPA PCIe product OPTION GT3G_1 0x6050 3G modem product OPTION GT3G_2 0x6100 3G modem product OPTION GT3G_3 0x6150 3G modem product OPTION GT3G_4 0x6200 3G modem product OPTION GT3G_5 0x6250 3G modem product OPTION GT3G_6 0x6350 3G modem product OPTION E6500 0x6500 3G modem product OPTION E6501 0x6501 3G modem product OPTION E6601 0x6601 3G modem product OPTION E6721 0x6721 3G modem product OPTION E6741 0x6741 3G modem product OPTION E6761 0x6761 3G modem product OPTION E6800 0x6800 3G modem product OPTION E7021 0x7021 3G modem product OPTION E7041 0x7041 3G modem product OPTION E7061 0x7061 3G modem product OPTION E7100 0x7100 3G modem product OPTION GTM380 0x7201 3G modem product OPTION GE40X 0x7601 Globetrotter HSUPA product OPTION GSICON72 0x6911 GlobeSurfer iCON product OPTION GSICONHSUPA 0x7251 Globetrotter HSUPA product OPTION ICON401 0x7401 GlobeSurfer iCON 401 product OPTION GTHSUPA 0x7011 Globetrotter HSUPA product OPTION GMT382 0x7501 Globetrotter HSUPA product OPTION GE40X_1 0x7301 Globetrotter HSUPA product OPTION GE40X_2 0x7361 Globetrotter HSUPA product OPTION GE40X_3 0x7381 Globetrotter HSUPA product OPTION GTM661W 0x9000 GTM661W product OPTION ICONEDGE 0xc031 GlobeSurfer iCON EDGE product OPTION MODHSXPA 0xd013 Globetrotter HSUPA product OPTION ICON321 0xd031 Globetrotter HSUPA product OPTION ICON505 0xd055 Globetrotter iCON 505 product OPTION ICON452 0x7901 Globetrotter iCON 452 /* Optoelectronics Co., Ltd */ product OPTO BARCODE 0x0001 Barcode Reader product OPTO OPTICONCODE 0x0009 Opticon Code Reader product OPTO BARCODE_1 0xa002 Barcode Reader product OPTO CRD7734 0xc000 USB Cradle CRD-7734-RU product OPTO CRD7734_1 0xc001 USB Cradle CRD-7734-RU /* OvisLink product */ product OVISLINK RT3072 0x3072 RT3072 /* OQO */ product OQO WIFI01 0x0002 model 01 WiFi interface product OQO BT01 0x0003 model 01 Bluetooth interface product OQO ETHER01PLUS 0x7720 model 01+ Ethernet product OQO ETHER01 0x8150 model 01 Ethernet interface /* Ours Technology Inc. */ product OTI DKU5 0x6858 DKU-5 Serial /* Owen.ru products */ product OWEN AC4 0x0004 AC4 USB-RS485 converter /* OWL producs */ product OWL CM_160 0xca05 OWL CM-160 power monitor /* Palm Computing, Inc. product */ product PALM SERIAL 0x0080 USB Serial product PALM M500 0x0001 Palm m500 product PALM M505 0x0002 Palm m505 product PALM M515 0x0003 Palm m515 product PALM I705 0x0020 Palm i705 product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z product PALM M125 0x0040 Palm m125 product PALM M130 0x0050 Palm m130 product PALM TUNGSTEN_T 0x0060 Palm Tungsten T product PALM ZIRE31 0x0061 Palm Zire 31 product PALM ZIRE 0x0070 Palm Zire /* Panasonic products */ product PANASONIC LS120CAM 0x0901 LS-120 Camera product PANASONIC KXL840AN 0x0d01 CD-R Drive KXL-840AN product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW product PANASONIC SDCAAE 0x1b00 MultiMediaCard product PANASONIC TYTP50P6S 0x3900 TY-TP50P6-S 50in Touch Panel /* Papouch products */ product PAPOUCH AD4USB 0x8003 FTDI compatible adapter product PAPOUCH AP485 0x0101 FTDI compatible adapter product PAPOUCH AP485_2 0x0104 FTDI compatible adapter product PAPOUCH DRAK5 0x0700 FTDI compatible adapter product PAPOUCH DRAK6 0x1000 FTDI compatible adapter product PAPOUCH GMSR 0x8005 FTDI compatible adapter product PAPOUCH GMUX 0x8004 FTDI compatible adapter product PAPOUCH IRAMP 0x0500 FTDI compatible adapter product PAPOUCH LEC 0x0300 FTDI compatible adapter product PAPOUCH MU 0x8001 FTDI compatible adapter product PAPOUCH QUIDO10X1 0x0b00 FTDI compatible adapter product PAPOUCH QUIDO2X16 0x0e00 FTDI compatible adapter product PAPOUCH QUIDO2X2 0x0a00 FTDI compatible adapter product PAPOUCH QUIDO30X3 0x0c00 FTDI compatible adapter product PAPOUCH QUIDO3X32 0x0f00 FTDI compatible adapter product PAPOUCH QUIDO4X4 0x0900 FTDI compatible adapter product PAPOUCH QUIDO60X3 0x0d00 FTDI compatible adapter product PAPOUCH QUIDO8X8 0x0800 FTDI compatible adapter product PAPOUCH SB232 0x0301 FTDI compatible adapter product PAPOUCH SB422 0x0102 FTDI compatible adapter product PAPOUCH SB422_2 0x0105 FTDI compatible adapter product PAPOUCH SB485 0x0100 FTDI compatible adapter product PAPOUCH SB485C 0x0107 FTDI compatible adapter product PAPOUCH SB485S 0x0106 FTDI compatible adapter product PAPOUCH SB485_2 0x0103 FTDI compatible adapter product PAPOUCH SIMUKEY 0x8002 FTDI compatible adapter product PAPOUCH TMU 0x0400 FTDI compatible adapter product PAPOUCH UPSUSB 0x8000 FTDI compatible adapter /* PARA Industrial products */ product PARA RT3070 0x8888 RT3070 /* Simtec Electronics products */ product SIMTEC ENTROPYKEY 0x0001 Entropy Key /* Pegatron products */ product PEGATRON RT2870 0x0002 RT2870 product PEGATRON RT3070 0x000c RT3070 product PEGATRON RT3070_2 0x000e RT3070 product PEGATRON RT3070_3 0x0010 RT3070 /* Peracom products */ product PERACOM SERIAL1 0x0001 Serial product PERACOM ENET 0x0002 Ethernet product PERACOM ENET3 0x0003 At Home Ethernet product PERACOM ENET2 0x0005 Ethernet /* Peraso Technologies, Inc products */ product PERASO PRS4001 0x4001 PRS4001 WLAN /* Philips products */ product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System product PHILIPS HUB 0x0201 hub product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera product PHILIPS SPC900NC 0x0329 SPC 900NC CCD PC Camera product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System product PHILIPS ACE1001 0x066a AKTAKOM ACE-1001 cable product PHILIPS SPE3030CC 0x083a USB 2.0 External Disk product PHILIPS SNU5600 0x1236 SNU5600 product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player product PHILIPS RT2870 0x200f RT2870 /* Philips Semiconductor products */ product PHILIPSSEMI HUB1122 0x1122 HUB /* Megatec */ product MEGATEC UPS 0x5161 Phoenixtec protocol based UPS /* P.I. Engineering products */ product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter /* Planex Communications products */ product PLANEX GW_US11H 0x14ea GW-US11H WLAN product PLANEX2 RTL8188CUS 0x1201 RTL8188CUS product PLANEX2 GW_US11S 0x3220 GW-US11S WLAN product PLANEX2 GW_US54GXS 0x5303 GW-US54GXS WLAN product PLANEX2 GW_US300 0x5304 GW-US300 product PLANEX2 RTL8188CU_1 0xab2a RTL8188CU product PLANEX2 RTL8188CU_2 0xed17 RTL8188CU product PLANEX2 RTL8188CU_3 0x4902 RTL8188CU product PLANEX2 RTL8188CU_4 0xab2e RTL8188CU product PLANEX2 RTL8192CU 0xab2b RTL8192CU product PLANEX2 GWUS54HP 0xab01 GW-US54HP product PLANEX2 GWUS300MINIS 0xab24 GW-US300MiniS product PLANEX2 RT3070 0xab25 RT3070 product PLANEX2 MZKUE150N 0xab2f MZK-UE150N product PLANEX2 GW900D 0xab30 GW-900D product PLANEX2 GWUS54MINI2 0xab50 GW-US54Mini2 product PLANEX2 GWUS54SG 0xc002 GW-US54SG product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL product PLANEX2 GWUS54GD 0xed01 GW-US54GD product PLANEX2 GWUSMM 0xed02 GW-USMM product PLANEX2 RT2870 0xed06 RT2870 product PLANEX2 GWUSMICRON 0xed14 GW-USMicroN product PLANEX2 GWUSVALUEEZ 0xed17 GW-USValue-EZ product PLANEX3 GWUS54GZ 0xab10 GW-US54GZ product PLANEX3 GU1000T 0xab11 GU-1000T product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini product PLANEX2 GWUSNANO 0xab28 GW-USNano /* Plextor Corp. */ product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U /* PLX products */ product PLX TESTBOARD 0x9060 test board product PLX CA42 0xac70 CA-42 /* PNY products */ product PNY ATTACHE2 0x0010 USB 2.0 Flash Drive /* PortGear products */ product PORTGEAR EA8 0x0008 Ethernet product PORTGEAR EA9 0x0009 Ethernet /* Portsmith products */ product PORTSMITH EEA 0x3003 Express Ethernet /* Posiflex products */ product POSIFLEX PP7000 0x0300 FTDI compatible adapter /* Primax products */ product PRIMAX G2X300 0x0300 G2-200 scanner product PRIMAX G2E300 0x0301 G2E-300 scanner product PRIMAX G2300 0x0302 G2-300 scanner product PRIMAX G2E3002 0x0303 G2E-300 scanner product PRIMAX 9600 0x0340 Colorado USB 9600 scanner product PRIMAX 600U 0x0341 Colorado 600u scanner product PRIMAX 6200 0x0345 Visioneer 6200 scanner product PRIMAX 19200 0x0360 Colorado USB 19200 scanner product PRIMAX 1200U 0x0361 Colorado 1200u scanner product PRIMAX G600 0x0380 G2-600 scanner product PRIMAX 636I 0x0381 ReadyScan 636i product PRIMAX G2600 0x0382 G2-600 scanner product PRIMAX G2E600 0x0383 G2E-600 scanner product PRIMAX COMFORT 0x4d01 Comfort product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1 product PRIMAX HP_RH304AA 0x4d17 HP RH304AA mouse /* Prolific products */ product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface product PROLIFIC MOTOROLA 0x0307 Motorola Cable product PROLIFIC RSAQ2 0x04bb PL2303 Serial (IODATA USB-RSAQ2) product PROLIFIC ALLTRONIX_GPRS 0x0609 Alltronix ACM003U00 modem product PROLIFIC ALDIGA_AL11U 0x0611 AlDiga AL-11U modem product PROLIFIC MICROMAX_610U 0x0612 Micromax 610U product PROLIFIC DCU11 0x1234 DCU-11 Phone Cable product PROLIFIC UIC_MSR206 0x206a UIC MSR206 Card Reader product PROLIFIC PL2303 0x2303 PL2303 Serial (ATEN/IOGEAR UC232A) product PROLIFIC PL2305 0x2305 Parallel printer product PROLIFIC ATAPI4 0x2307 ATAPI-4 Controller product PROLIFIC PL2501 0x2501 PL2501 Host-Host interface product PROLIFIC PL2506 0x2506 PL2506 USB to IDE Bridge product PROLIFIC PL27A1 0x27A1 PL27A1 USB 3.0 Host-Host interface product PROLIFIC HCR331 0x331a HCR331 Hybrid Card Reader product PROLIFIC PHAROS 0xaaa0 Prolific Pharos product PROLIFIC RSAQ3 0xaaa2 PL2303 Serial Adapter (IODATA USB-RSAQ3) product PROLIFIC2 PL2303 0x2303 PL2303 Serial Adapter /* Putercom products */ product PUTERCOM UPA100 0x047e USB-1284 BRIDGE /* Qcom products */ product QCOM RT2573 0x6196 RT2573 product QCOM RT2573_2 0x6229 RT2573 product QCOM RT2573_3 0x6238 RT2573 product QCOM RT2870 0x6259 RT2870 /* QI-hardware */ product QIHARDWARE JTAGSERIAL 0x0713 FTDI compatible adapter /* Qisda products */ product QISDA H21_1 0x4512 3G modem product QISDA H21_2 0x4523 3G modem product QISDA H20_1 0x4515 3G modem product QISDA H20_2 0x4519 3G modem /* Qualcomm products */ product QUALCOMM CDMA_MSM 0x6000 CDMA Technologies MSM phone product QUALCOMM NTT_L02C_MODEM 0x618f NTT DOCOMO L-02C product QUALCOMM NTT_L02C_STORAGE 0x61dd NTT DOCOMO L-02C product QUALCOMM2 MF330 0x6613 MF330 product QUALCOMM2 RWT_FCT 0x3100 RWT FCT-CDMA 2000 1xRTT modem product QUALCOMM2 CDMA_MSM 0x3196 CDMA Technologies MSM modem product QUALCOMM2 AC8700 0x6000 AC8700 product QUALCOMM2 VW110L 0x1000 Vertex Wireless 110L modem product QUALCOMM2 SIM5218 0x9000 SIM5218 product QUALCOMM2 WM620 0x9002 Neoway WM620 product QUALCOMM2 GOBI2000_QDL 0x9204 Qualcomm Gobi 2000 QDL product QUALCOMM2 GOBI2000 0x9205 Qualcomm Gobi 2000 modem product QUALCOMM2 VT80N 0x6500 Venus VT80N product QUALCOMM3 VFAST2 0x9909 Venus Fast2 modem product QUALCOMMINC CDMA_MSM 0x0001 CDMA Technologies MSM modem product QUALCOMMINC E0002 0x0002 3G modem product QUALCOMMINC E0003 0x0003 3G modem product QUALCOMMINC E0004 0x0004 3G modem product QUALCOMMINC E0005 0x0005 3G modem product QUALCOMMINC E0006 0x0006 3G modem product QUALCOMMINC E0007 0x0007 3G modem product QUALCOMMINC E0008 0x0008 3G modem product QUALCOMMINC E0009 0x0009 3G modem product QUALCOMMINC E000A 0x000a 3G modem product QUALCOMMINC E000B 0x000b 3G modem product QUALCOMMINC E000C 0x000c 3G modem product QUALCOMMINC E000D 0x000d 3G modem product QUALCOMMINC E000E 0x000e 3G modem product QUALCOMMINC E000F 0x000f 3G modem product QUALCOMMINC E0010 0x0010 3G modem product QUALCOMMINC E0011 0x0011 3G modem product QUALCOMMINC E0012 0x0012 3G modem product QUALCOMMINC E0013 0x0013 3G modem product QUALCOMMINC E0014 0x0014 3G modem product QUALCOMMINC MF628 0x0015 3G modem product QUALCOMMINC MF633R 0x0016 ZTE WCDMA modem product QUALCOMMINC E0017 0x0017 3G modem product QUALCOMMINC E0018 0x0018 3G modem product QUALCOMMINC E0019 0x0019 3G modem product QUALCOMMINC E0020 0x0020 3G modem product QUALCOMMINC E0021 0x0021 3G modem product QUALCOMMINC E0022 0x0022 3G modem product QUALCOMMINC E0023 0x0023 3G modem product QUALCOMMINC E0024 0x0024 3G modem product QUALCOMMINC E0025 0x0025 3G modem product QUALCOMMINC E0026 0x0026 3G modem product QUALCOMMINC E0027 0x0027 3G modem product QUALCOMMINC E0028 0x0028 3G modem product QUALCOMMINC E0029 0x0029 3G modem product QUALCOMMINC E0030 0x0030 3G modem product QUALCOMMINC MF626 0x0031 3G modem product QUALCOMMINC E0032 0x0032 3G modem product QUALCOMMINC E0033 0x0033 3G modem product QUALCOMMINC E0037 0x0037 3G modem product QUALCOMMINC E0039 0x0039 3G modem product QUALCOMMINC E0042 0x0042 3G modem product QUALCOMMINC E0043 0x0043 3G modem product QUALCOMMINC E0048 0x0048 3G modem product QUALCOMMINC E0049 0x0049 3G modem product QUALCOMMINC E0051 0x0051 3G modem product QUALCOMMINC E0052 0x0052 3G modem product QUALCOMMINC ZTE_STOR2 0x0053 USB ZTE Storage product QUALCOMMINC E0054 0x0054 3G modem product QUALCOMMINC E0055 0x0055 3G modem product QUALCOMMINC E0057 0x0057 3G modem product QUALCOMMINC E0058 0x0058 3G modem product QUALCOMMINC E0059 0x0059 3G modem product QUALCOMMINC E0060 0x0060 3G modem product QUALCOMMINC E0061 0x0061 3G modem product QUALCOMMINC E0062 0x0062 3G modem product QUALCOMMINC E0063 0x0063 3G modem product QUALCOMMINC E0064 0x0064 3G modem product QUALCOMMINC E0066 0x0066 3G modem product QUALCOMMINC E0069 0x0069 3G modem product QUALCOMMINC E0070 0x0070 3G modem product QUALCOMMINC E0073 0x0073 3G modem product QUALCOMMINC E0076 0x0076 3G modem product QUALCOMMINC E0078 0x0078 3G modem product QUALCOMMINC E0082 0x0082 3G modem product QUALCOMMINC E0086 0x0086 3G modem product QUALCOMMINC MF112 0x0103 3G modem product QUALCOMMINC SURFSTICK 0x0117 1&1 Surf Stick product QUALCOMMINC K3772_Z_INIT 0x1179 K3772-Z Initial product QUALCOMMINC K3772_Z 0x1181 K3772-Z product QUALCOMMINC ZTE_MF730M 0x1420 3G modem product QUALCOMMINC MF195E_INIT 0x1514 MF195E initial product QUALCOMMINC MF195E 0x1516 MF195E product QUALCOMMINC ZTE_STOR 0x2000 USB ZTE Storage product QUALCOMMINC E2002 0x2002 3G modem product QUALCOMMINC E2003 0x2003 3G modem product QUALCOMMINC AC682 0xffdd CDMA 1xEVDO USB modem product QUALCOMMINC AC682_INIT 0xffde CDMA 1xEVDO USB modem (initial) product QUALCOMMINC AC8710 0xfff1 3G modem product QUALCOMMINC AC2726 0xfff5 3G modem product QUALCOMMINC AC8700 0xfffe CDMA 1xEVDO USB modem /* Quanta products */ product QUANTA RW6815_1 0x00ce HP iPAQ rw6815 product QUANTA RT3070 0x0304 RT3070 product QUANTA Q101_STOR 0x1000 USB Q101 Storage product QUANTA Q101 0xea02 HSDPA modem product QUANTA Q111 0xea03 HSDPA modem product QUANTA GLX 0xea04 HSDPA modem product QUANTA GKE 0xea05 HSDPA modem product QUANTA GLE 0xea06 HSDPA modem product QUANTA RW6815R 0xf003 HP iPAQ rw6815 RNDIS /* Quectel products */ product QUECTEL EC25 0x0125 LTE modem /* Quickshot products */ product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad /* Qtronix products */ product QTRONIX 980N 0x2011 Scorpion-980N keyboard /* Radio Shack */ product RADIOSHACK USBCABLE 0x4026 USB to Serial Cable /* Rainbow Technologies products */ product RAINBOW IKEY2000 0x1200 i-Key 2000 /* Ralink Technology products */ product RALINK RT2570 0x1706 RT2500USB Wireless Adapter product RALINK RT2070 0x2070 RT2070 product RALINK RT2570_2 0x2570 RT2500USB Wireless Adapter product RALINK RT2573 0x2573 RT2501USB Wireless Adapter product RALINK RT2671 0x2671 RT2601USB Wireless Adapter product RALINK RT2770 0x2770 RT2770 product RALINK RT2870 0x2870 RT2870 product RALINK RT_STOR 0x2878 USB Storage product RALINK RT3070 0x3070 RT3070 product RALINK RT3071 0x3071 RT3071 product RALINK RT3072 0x3072 RT3072 product RALINK RT3370 0x3370 RT3370 product RALINK RT3572 0x3572 RT3572 product RALINK RT3573 0x3573 RT3573 product RALINK RT5370 0x5370 RT5370 product RALINK RT5372 0x5372 RT5372 product RALINK RT5572 0x5572 RT5572 product RALINK RT8070 0x8070 RT8070 product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter /* RATOC Systems products */ product RATOC REXUSB60 0xb000 USB serial adapter REX-USB60 product RATOC REXUSB60F 0xb020 USB serial adapter REX-USB60F /* Realtek products */ /* Green House and CompUSA OEM this part */ product REALTEK DUMMY 0x0000 Dummy product product REALTEK USB20CRW 0x0158 USB20CRW Card Reader product REALTEK RTL8188ETV 0x0179 RTL8188ETV product REALTEK RTL8188CTV 0x018a RTL8188CTV product REALTEK RTL8188RU_2 0x317f RTL8188RU product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet product REALTEK RTL8152 0x8152 RTL8152 USB Ethernet product REALTEK RTL8153 0x8153 RTL8153 USB Ethernet product REALTEK RTL8188CE_0 0x8170 RTL8188CE product REALTEK RTL8171 0x8171 RTL8171 product REALTEK RTL8172 0x8172 RTL8172 product REALTEK RTL8173 0x8173 RTL8173 product REALTEK RTL8174 0x8174 RTL8174 product REALTEK RTL8188CU_0 0x8176 RTL8188CU product REALTEK RTL8191CU 0x8177 RTL8191CU product REALTEK RTL8192CU 0x8178 RTL8192CU product REALTEK RTL8188EU 0x8179 RTL8188EU product REALTEK RTL8188CU_1 0x817a RTL8188CU product REALTEK RTL8188CU_2 0x817b RTL8188CU product REALTEK RTL8192CE 0x817c RTL8192CE product REALTEK RTL8188RU_1 0x817d RTL8188RU product REALTEK RTL8188CE_1 0x817e RTL8188CE product REALTEK RTL8188RU_3 0x817f RTL8188RU product REALTEK RTL8187 0x8187 RTL8187 Wireless Adapter product REALTEK RTL8187B_0 0x8189 RTL8187B Wireless Adapter product REALTEK RTL8188CUS 0x818a RTL8188CUS product REALTEK RTL8192EU 0x818b RTL8192EU product REALTEK RTL8188CU_3 0x8191 RTL8188CU product REALTEK RTL8196EU 0x8196 RTL8196EU product REALTEK RTL8187B_1 0x8197 RTL8187B Wireless Adapter product REALTEK RTL8187B_2 0x8198 RTL8187B Wireless Adapter product REALTEK RTL8712 0x8712 RTL8712 product REALTEK RTL8713 0x8713 RTL8713 product REALTEK RTL8188CU_COMBO 0x8754 RTL8188CU product REALTEK RTL8723BU 0xb720 RTL8723BU product REALTEK RTL8192SU 0xc512 RTL8192SU product REALTEK RTL8812AU 0x8812 RTL8812AU Wireless Adapter /* RedOctane products */ product REDOCTANE DUMMY 0x0000 Dummy product product REDOCTANE GHMIDI 0x474b GH MIDI INTERFACE /* Renesas products */ product RENESAS RX610 0x0053 RX610 RX-Stick /* Ricoh products */ product RICOH VGPVCC2 0x1830 VGP-VCC2 Camera product RICOH VGPVCC3 0x1832 VGP-VCC3 Camera product RICOH VGPVCC2_2 0x1833 VGP-VCC2 Camera product RICOH VGPVCC2_3 0x1834 VGP-VCC2 Camera product RICOH VGPVCC7 0x183a VGP-VCC7 Camera product RICOH VGPVCC8 0x183b VGP-VCC8 Camera /* Reiner-SCT products */ product REINERSCT CYBERJACK_ECOM 0x0100 e-com cyberJack /* Roland products */ product ROLAND UA100 0x0000 UA-100 Audio I/F product ROLAND UM4 0x0002 UM-4 MIDI I/F product ROLAND SC8850 0x0003 SC-8850 MIDI Synth product ROLAND U8 0x0004 U-8 Audio I/F product ROLAND UM2 0x0005 UM-2 MIDI I/F product ROLAND SC8820 0x0007 SC-8820 MIDI Synth product ROLAND PC300 0x0008 PC-300 MIDI Keyboard product ROLAND UM1 0x0009 UM-1 MIDI I/F product ROLAND SK500 0x000b SK-500 MIDI Keyboard product ROLAND SCD70 0x000c SC-D70 MIDI Synth product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native) product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic) product ROLAND SD90 0x0016 SD-90 MIDI Synth product ROLAND UM550 0x0023 UM-550 MIDI I/F product ROLAND SD20 0x0027 SD-20 MIDI Synth product ROLAND SD80 0x0029 SD-80 MIDI Synth product ROLAND UA700 0x002b UA-700 Audio I/F product ROLAND PCR300 0x0033 EDIROL PCR-300 MIDI I/F /* Rockfire products */ product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB /* RATOC Systems products */ product RATOC REXUSB60 0xb000 REX-USB60 product RATOC REXUSB60F 0xb020 REX-USB60F /* RT system products */ product RTSYSTEMS CT29B 0x9e54 FTDI compatible adapter product RTSYSTEMS SERIAL_VX7 0x9e52 FTDI compatible adapter /* Sagem products */ product SAGEM USBSERIAL 0x0027 USB-Serial Controller product SAGEM XG760A 0x004a XG-760A product SAGEM XG76NA 0x0062 XG-76NA /* Samsung products */ product SAMSUNG WIS09ABGN 0x2018 WIS09ABGN Wireless LAN adapter product SAMSUNG ML6060 0x3008 ML-6060 laser printer product SAMSUNG YP_U2 0x5050 YP-U2 MP3 Player product SAMSUNG YP_U4 0x5092 YP-U4 MP3 Player product SAMSUNG I500 0x6601 I500 Palm USB Phone product SAMSUNG I330 0x8001 I330 phone cradle product SAMSUNG2 RT2870_1 0x2018 RT2870 /* Samsung Techwin products */ product SAMSUNG_TECHWIN DIGIMAX_410 0x000a Digimax 410 /* SanDisk products */ product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a product SANDISK SDDR31 0x0002 ImageMate SDDR-31 product SANDISK SDDR05 0x0005 ImageMate SDDR-05 product SANDISK SDDR12 0x0100 ImageMate SDDR-12 product SANDISK SDDR09 0x0200 ImageMate SDDR-09 product SANDISK SDDR75 0x0810 ImageMate SDDR-75 product SANDISK SDCZ2_128 0x7100 Cruzer Mini 128MB product SANDISK SDCZ2_256 0x7104 Cruzer Mini 256MB product SANDISK SDCZ4_128 0x7112 Cruzer Micro 128MB product SANDISK SDCZ4_256 0x7113 Cruzer Micro 256MB product SANDISK IMAGEMATE_SDDR289 0xb6ba ImageMate SDDR-289 /* Sanwa Electric Instrument Co., Ltd. products */ product SANWA KB_USB2 0x0701 KB-USB2 multimeter cable /* Sanyo Electric products */ product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone /* ScanLogic products */ product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner /* Schweitzer Engineering Laboratories products */ product SEL C662 0x0001 C662 Cable /* Sealevel products */ product SEALEVEL 2101 0x2101 FTDI compatible adapter product SEALEVEL 2102 0x2102 FTDI compatible adapter product SEALEVEL 2103 0x2103 FTDI compatible adapter product SEALEVEL 2104 0x2104 FTDI compatible adapter product SEALEVEL 2106 0x9020 FTDI compatible adapter product SEALEVEL 2201_1 0x2211 FTDI compatible adapter product SEALEVEL 2201_2 0x2221 FTDI compatible adapter product SEALEVEL 2202_1 0x2212 FTDI compatible adapter product SEALEVEL 2202_2 0x2222 FTDI compatible adapter product SEALEVEL 2203_1 0x2213 FTDI compatible adapter product SEALEVEL 2203_2 0x2223 FTDI compatible adapter product SEALEVEL 2401_1 0x2411 FTDI compatible adapter product SEALEVEL 2401_2 0x2421 FTDI compatible adapter product SEALEVEL 2401_3 0x2431 FTDI compatible adapter product SEALEVEL 2401_4 0x2441 FTDI compatible adapter product SEALEVEL 2402_1 0x2412 FTDI compatible adapter product SEALEVEL 2402_2 0x2422 FTDI compatible adapter product SEALEVEL 2402_3 0x2432 FTDI compatible adapter product SEALEVEL 2402_4 0x2442 FTDI compatible adapter product SEALEVEL 2403_1 0x2413 FTDI compatible adapter product SEALEVEL 2403_2 0x2423 FTDI compatible adapter product SEALEVEL 2403_3 0x2433 FTDI compatible adapter product SEALEVEL 2403_4 0x2443 FTDI compatible adapter product SEALEVEL 2801_1 0x2811 FTDI compatible adapter product SEALEVEL 2801_2 0x2821 FTDI compatible adapter product SEALEVEL 2801_3 0x2831 FTDI compatible adapter product SEALEVEL 2801_4 0x2841 FTDI compatible adapter product SEALEVEL 2801_5 0x2851 FTDI compatible adapter product SEALEVEL 2801_6 0x2861 FTDI compatible adapter product SEALEVEL 2801_7 0x2871 FTDI compatible adapter product SEALEVEL 2801_8 0x2881 FTDI compatible adapter product SEALEVEL 2802_1 0x2812 FTDI compatible adapter product SEALEVEL 2802_2 0x2822 FTDI compatible adapter product SEALEVEL 2802_3 0x2832 FTDI compatible adapter product SEALEVEL 2802_4 0x2842 FTDI compatible adapter product SEALEVEL 2802_5 0x2852 FTDI compatible adapter product SEALEVEL 2802_6 0x2862 FTDI compatible adapter product SEALEVEL 2802_7 0x2872 FTDI compatible adapter product SEALEVEL 2802_8 0x2882 FTDI compatible adapter product SEALEVEL 2803_1 0x2813 FTDI compatible adapter product SEALEVEL 2803_2 0x2823 FTDI compatible adapter product SEALEVEL 2803_3 0x2833 FTDI compatible adapter product SEALEVEL 2803_4 0x2843 FTDI compatible adapter product SEALEVEL 2803_5 0x2853 FTDI compatible adapter product SEALEVEL 2803_6 0x2863 FTDI compatible adapter product SEALEVEL 2803_7 0x2873 FTDI compatible adapter product SEALEVEL 2803_8 0x2883 FTDI compatible adapter /* Senao products */ product SENAO EUB1200AC 0x0100 EnGenius EUB1200AC product SENAO RT2870_3 0x0605 RT2870 product SENAO RT2870_4 0x0615 RT2870 product SENAO NUB8301 0x2000 NUB-8301 product SENAO RT2870_1 0x9701 RT2870 product SENAO RT2870_2 0x9702 RT2870 product SENAO RT3070 0x9703 RT3070 product SENAO RT3071 0x9705 RT3071 product SENAO RT3072_1 0x9706 RT3072 product SENAO RT3072_2 0x9707 RT3072 product SENAO RT3072_3 0x9708 RT3072 product SENAO RT3072_4 0x9709 RT3072 product SENAO RT3072_5 0x9801 RT3072 product SENAO RTL8192SU_1 0x9603 RTL8192SU product SENAO RTL8192SU_2 0x9605 RTL8192SU /* ShanTou products */ product SHANTOU ST268 0x0268 ST268 product SHANTOU DM9601 0x9601 DM 9601 product SHANTOU ADM8515 0x8515 ADM8515 /* Shark products */ product SHARK PA 0x0400 Pocket Adapter /* Sharp products */ product SHARP SL5500 0x8004 Zaurus SL-5500 PDA product SHARP SLA300 0x8005 Zaurus SL-A300 PDA product SHARP SL5600 0x8006 Zaurus SL-5600 PDA product SHARP SLC700 0x8007 Zaurus SL-C700 PDA product SHARP SLC750 0x9031 Zaurus SL-C750 PDA product SHARP WZERO3ES 0x9123 W-ZERO3 ES Smartphone product SHARP WZERO3ADES 0x91ac Advanced W-ZERO3 ES Smartphone product SHARP WILLCOM03 0x9242 WILLCOM03 /* Shuttle Technology products */ product SHUTTLE EUSB 0x0001 E-USB Bridge product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge product SHUTTLE SDDR09 0x0003 ImageMate SDDR09 product SHUTTLE EUSBCFSM 0x0005 eUSB SmartMedia / CompactFlash Adapter product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter product SHUTTLE HIFD 0x0007 Sony Hifd product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter product SHUTTLE CF 0x000a eUSB CompactFlash Adapter product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge product SHUTTLE CDRW 0x0101 CD-RW Device product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader /* Siemens products */ product SIEMENS SPEEDSTREAM 0x1001 SpeedStream product SIEMENS SPEEDSTREAM22 0x1022 SpeedStream 1022 product SIEMENS2 WLL013 0x001b WLL013 product SIEMENS2 ES75 0x0034 GSM module MC35 product SIEMENS2 WL54G 0x3c06 54g USB Network Adapter product SIEMENS3 SX1 0x0001 SX1 product SIEMENS3 X65 0x0003 X65 product SIEMENS3 X75 0x0004 X75 product SIEMENS3 EF81 0x0005 EF81 /* Sierra Wireless products */ product SIERRA EM5625 0x0017 EM5625 product SIERRA MC5720_2 0x0018 MC5720 product SIERRA MC5725 0x0020 MC5725 product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580 product SIERRA AIRCARD595 0x0019 Sierra Wireless AirCard 595 product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U product SIERRA AC597E 0x0021 Sierra Wireless AirCard 597E product SIERRA EM5725 0x0022 EM5725 product SIERRA C597 0x0023 Sierra Wireless Compass 597 product SIERRA MC5727 0x0024 MC5727 product SIERRA T598 0x0025 T598 product SIERRA T11 0x0026 T11 product SIERRA AC402 0x0027 AC402 product SIERRA MC5728 0x0028 MC5728 product SIERRA E0029 0x0029 E0029 product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580 product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U product SIERRA MC5720 0x0218 MC5720 Wireless Modem product SIERRA MINI5725 0x0220 Sierra Wireless miniPCI 5275 product SIERRA MC5727_2 0x0224 MC5727 product SIERRA MC8755_2 0x6802 MC8755 product SIERRA MC8765 0x6803 MC8765 product SIERRA MC8755 0x6804 MC8755 product SIERRA MC8765_2 0x6805 MC8765 product SIERRA MC8755_4 0x6808 MC8755 product SIERRA MC8765_3 0x6809 MC8765 product SIERRA AC875U 0x6812 AC875U HSDPA USB Modem product SIERRA MC8755_3 0x6813 MC8755 HSDPA product SIERRA MC8775_2 0x6815 MC8775 product SIERRA MC8775 0x6816 MC8775 product SIERRA AC875 0x6820 Sierra Wireless AirCard 875 product SIERRA AC875U_2 0x6821 AC875U product SIERRA AC875E 0x6822 AC875E product SIERRA MC8780 0x6832 MC8780 product SIERRA MC8781 0x6833 MC8781 product SIERRA MC8780_2 0x6834 MC8780 product SIERRA MC8781_2 0x6835 MC8781 product SIERRA MC8780_3 0x6838 MC8780 product SIERRA MC8781_3 0x6839 MC8781 product SIERRA MC8785 0x683A MC8785 product SIERRA MC8785_2 0x683B MC8785 product SIERRA MC8790 0x683C MC8790 product SIERRA MC8791 0x683D MC8791 product SIERRA MC8792 0x683E MC8792 product SIERRA AC880 0x6850 Sierra Wireless AirCard 880 product SIERRA AC881 0x6851 Sierra Wireless AirCard 881 product SIERRA AC880E 0x6852 Sierra Wireless AirCard 880E product SIERRA AC881E 0x6853 Sierra Wireless AirCard 881E product SIERRA AC880U 0x6855 Sierra Wireless AirCard 880U product SIERRA AC881U 0x6856 Sierra Wireless AirCard 881U product SIERRA AC885E 0x6859 AC885E product SIERRA AC885E_2 0x685A AC885E product SIERRA AC885U 0x6880 Sierra Wireless AirCard 885U product SIERRA C888 0x6890 C888 product SIERRA C22 0x6891 C22 product SIERRA E6892 0x6892 E6892 product SIERRA E6893 0x6893 E6893 product SIERRA MC8700 0x68A3 MC8700 product SIERRA MC7354 0x68C0 MC7354 product SIERRA MC7355 0x9041 MC7355 product SIERRA MC7430 0x9071 Sierra Wireless MC7430 Qualcomm Snapdragon X7 LTE-A product SIERRA AC313U 0x68aa Sierra Wireless AirCard 313U product SIERRA TRUINSTALL 0x0fff Aircard Tru Installer /* Sigmatel products */ product SIGMATEL WBT_3052 0x4200 WBT-3052 IrDA/USB Bridge product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player /* SIIG products */ /* Also: Omnidirectional Control Technology products */ product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader product SIIG WINTERREADER 0x0330 WINTERREADER Reader product SIIG2 DK201 0x0103 FTDI compatible adapter product SIIG2 USBTOETHER 0x0109 USB TO Ethernet product SIIG2 US2308 0x0421 Serial /* Silicom products */ product SILICOM U2E 0x0001 U2E product SILICOM GPE 0x0002 Psion Gold Port Ethernet /* SI Labs */ product SILABS VSTABI 0x0f91 VStabi Controller product SILABS ARKHAM_DS101_M 0x1101 Arkham DS101 Monitor product SILABS ARKHAM_DS101_A 0x1601 Arkham DS101 Adapter product SILABS BSM7DUSB 0x800a SPORTident BSM7-D USB product SILABS POLOLU 0x803b Pololu Serial product SILABS CYGNAL_DEBUG 0x8044 Cygnal Debug Adapter product SILABS SB_PARAMOUNT_ME 0x8043 Software Bisque Paramount ME product SILABS SAEL 0x8053 SA-EL USB product SILABS GSM2228 0x8054 Enfora GSM2228 USB product SILABS ARGUSISP 0x8066 Argussoft ISP product SILABS IMS_USB_RS422 0x806f IMS USB-RS422 product SILABS CRUMB128 0x807a Crumb128 board product SILABS OPTRIS_MSPRO 0x80c4 Optris MSpro LT Thermometer product SILABS DEGREE 0x80ca Degree Controls Inc product SILABS TRACIENT 0x80dd Tracient RFID product SILABS TRAQMATE 0x80ed Track Systems Traqmate product SILABS SUUNTO 0x80f6 Suunto Sports Instrument product SILABS ARYGON_MIFARE 0x8115 Arygon Mifare RFID reader product SILABS BURNSIDE 0x813d Burnside Telecon Deskmobile product SILABS TAMSMASTER 0x813f Tams Master Easy Control product SILABS WMRBATT 0x814a WMR RIGblaster Plug&Play product SILABS WMRRIGBLASTER 0x814a WMR RIGblaster Plug&Play product SILABS WMRRIGTALK 0x814b WMR RIGtalk RT1 product SILABS B_G_H3000 0x8156 B&G H3000 Data Cable product SILABS HELICOM 0x815e Helicomm IP-Link 1220-DVM product SILABS HAMLINKUSB 0x815f Timewave HamLinkUSB product SILABS AVIT_USB_TTL 0x818b AVIT Research USB-TTL product SILABS MJS_TOSLINK 0x819f MJS USB-TOSLINK product SILABS WAVIT 0x81a6 ThinkOptics WavIt product SILABS MULTIPLEX_RC 0x81a9 Multiplex RC adapter product SILABS MSD_DASHHAWK 0x81ac MSD DashHawk product SILABS INSYS_MODEM 0x81ad INSYS Modem product SILABS LIPOWSKY_JTAG 0x81c8 Lipowsky Baby-JTAG product SILABS LIPOWSKY_LIN 0x81e2 Lipowsky Baby-LIN product SILABS AEROCOMM 0x81e7 Aerocomm Radio product SILABS ZEPHYR_BIO 0x81e8 Zephyr Bioharness product SILABS EMS_C1007 0x81f2 EMS C1007 HF RFID controller product SILABS LIPOWSKY_HARP 0x8218 Lipowsky HARP-1 product SILABS C2_EDGE_MODEM 0x822b Commander 2 EDGE(GSM) Modem product SILABS CYGNAL_GPS 0x826b Cygnal Fasttrax GPS product SILABS TELEGESIS_ETRX2 0x8293 Telegesis ETRX2USB product SILABS PROCYON_AVS 0x82f9 Procyon AVS product SILABS MC35PU 0x8341 MC35pu product SILABS CYGNAL 0x8382 Cygnal product SILABS AMBER_AMB2560 0x83a8 Amber Wireless AMB2560 product SILABS DEKTEK_DTAPLUS 0x83d8 DekTec DTA Plus VHF/UHF Booster product SILABS KYOCERA_GPS 0x8411 Kyocera GPS product SILABS IRZ_SG10 0x8418 IRZ SG-10 GSM/GPRS Modem product SILABS BEI_VCP 0x846e BEI USB Sensor (VCP) product SILABS BALLUFF_RFID 0x8477 Balluff RFID reader product SILABS AC_SERV_IBUS 0x85ea AC-Services IBUS Interface product SILABS AC_SERV_CIS 0x85eb AC-Services CIS-IBUS product SILABS V_PREON32 0x85f8 Virtenio Preon32 product SILABS AC_SERV_CAN 0x8664 AC-Services CAN Interface product SILABS AC_SERV_OBD 0x8665 AC-Services OBD Interface product SILABS MMB_ZIGBEE 0x88a4 MMB Networks ZigBee product SILABS INGENI_ZIGBEE 0x88a5 Planet Innovation Ingeni ZigBee product SILABS HUBZ 0x8a2a HubZ dual ZigBee and Z-Wave product SILABS CP2102 0xea60 SILABS USB UART product SILABS CP210X_2 0xea61 CP210x Serial product SILABS CP210X_3 0xea70 CP210x Serial product SILABS CP210X_4 0xea80 CP210x Serial product SILABS INFINITY_MIC 0xea71 Infinity GPS-MIC-1 Radio Monophone product SILABS USBSCOPE50 0xf001 USBscope50 product SILABS USBWAVE12 0xf002 USBwave12 product SILABS USBPULSE100 0xf003 USBpulse100 product SILABS USBCOUNT50 0xf004 USBcount50 product SILABS2 DCU11CLONE 0xaa26 DCU-11 clone product SILABS3 GPRS_MODEM 0xea61 GPRS Modem product SILABS4 100EU_MODEM 0xea61 GPRS Modem 100EU /* Silicon Portals Inc. */ product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware) product SILICONPORTALS YAPPHONE 0x0201 YAP Phone /* Sirius Technologies products */ product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB /* Sitecom products */ product SITECOM LN029 0x182d USB 2.0 Ethernet product SITECOM SERIAL 0x2068 USB to serial cable (v2) product SITECOM2 WL022 0x182d WL-022 /* Sitecom Europe products */ product SITECOMEU RT2870_1 0x0017 RT2870 product SITECOMEU WL168V1 0x000d WL-168 v1 product SITECOMEU LN030 0x0021 MCS7830 product SITECOMEU WL168V4 0x0028 WL-168 v4 product SITECOMEU RT2870_2 0x002b RT2870 product SITECOMEU RT2870_3 0x002c RT2870 product SITECOMEU RT2870_4 0x002d RT2870 product SITECOMEU RT2770 0x0039 RT2770 product SITECOMEU RT3070_2 0x003b RT3070 product SITECOMEU RT3070_3 0x003c RT3070 product SITECOMEU RT3070_4 0x003d RT3070 product SITECOMEU RT3070 0x003e RT3070 product SITECOMEU WL608 0x003f WL-608 product SITECOMEU RT3071 0x0040 RT3071 product SITECOMEU RT3072_1 0x0041 RT3072 product SITECOMEU RT3072_2 0x0042 RT3072 product SITECOMEU WL353 0x0045 WL-353 product SITECOMEU RT3072_3 0x0047 RT3072 product SITECOMEU RT3072_4 0x0048 RT3072 product SITECOMEU RT3072_5 0x004a RT3072 product SITECOMEU WL349V1 0x004b WL-349 v1 product SITECOMEU RT3072_6 0x004d RT3072 +product SITECOMEU WLA1000 0x005b WLA-1000 product SITECOMEU RTL8188CU_1 0x0052 RTL8188CU product SITECOMEU RTL8188CU_2 0x005c RTL8188CU product SITECOMEU RTL8192CU 0x0061 RTL8192CU product SITECOMEU LN032 0x0072 LN-032 product SITECOMEU WLA7100 0x0074 WLA-7100 product SITECOMEU LN031 0x0056 LN-031 product SITECOMEU LN028 0x061c LN-028 product SITECOMEU WL113 0x9071 WL-113 product SITECOMEU ZD1211B 0x9075 ZD1211B product SITECOMEU WL172 0x90ac WL-172 product SITECOMEU WL113R2 0x9712 WL-113 rev 2 /* Skanhex Technology products */ product SKANHEX MD_7425 0x410a MD 7425 Camera product SKANHEX SX_520Z 0x5200 SX 520z Camera /* Smart Technologies products */ product SMART PL2303 0x2303 Serial adapter /* SmartBridges products */ product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB Ethernet product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP Ethernet /* SMC products */ product SMC 2102USB 0x0100 10Mbps Ethernet product SMC 2202USB 0x0200 10/100 Ethernet product SMC 2206USB 0x0201 EZ Connect USB Ethernet product SMC 2862WG 0xee13 EZ Connect Wireless Adapter product SMC2 2020HUB 0x2020 USB Hub product SMC2 2514HUB 0x2514 USB Hub product SMC3 2662WUSB 0xa002 2662W-AR Wireless product SMC2 LAN7800_ETH 0x7800 USB/Ethernet product SMC2 LAN7801_ETH 0x7801 USB/Ethernet product SMC2 LAN7850_ETH 0x7850 USB/Ethernet product SMC2 LAN9500_ETH 0x9500 USB/Ethernet product SMC2 LAN9505_ETH 0x9505 USB/Ethernet product SMC2 LAN9530_ETH 0x9530 USB/Ethernet product SMC2 LAN9730_ETH 0x9730 USB/Ethernet product SMC2 LAN9500_SAL10 0x9900 USB/Ethernet product SMC2 LAN9505_SAL10 0x9901 USB/Ethernet product SMC2 LAN9500A_SAL10 0x9902 USB/Ethernet product SMC2 LAN9505A_SAL10 0x9903 USB/Ethernet product SMC2 LAN9514_SAL10 0x9904 USB/Ethernet product SMC2 LAN9500A_HAL 0x9905 USB/Ethernet product SMC2 LAN9505A_HAL 0x9906 USB/Ethernet product SMC2 LAN9500_ETH_2 0x9907 USB/Ethernet product SMC2 LAN9500A_ETH_2 0x9908 USB/Ethernet product SMC2 LAN9514_ETH_2 0x9909 USB/Ethernet product SMC2 LAN9500A_ETH 0x9e00 USB/Ethernet product SMC2 LAN9505A_ETH 0x9e01 USB/Ethernet product SMC2 LAN89530_ETH 0x9e08 USB/Ethernet product SMC2 LAN9514_ETH 0xec00 USB/Ethernet /* SOHOware products */ product SOHOWARE NUB100 0x9100 10/100 USB Ethernet product SOHOWARE NUB110 0x9110 10/100 USB Ethernet /* SOLID YEAR products */ product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard /* SONY products */ product SONY DSC 0x0010 DSC cameras product SONY MS_NW_MS7 0x0025 Memorystick NW-MS7 product SONY PORTABLE_HDD_V2 0x002b Portable USB Harddrive V2 product SONY MSACUS1 0x002d Memorystick MSAC-US1 product SONY HANDYCAM 0x002e Handycam product SONY MSC 0x0032 MSC memory stick slot product SONY CLIE_35 0x0038 Sony Clie v3.5 product SONY MS_PEG_N760C 0x0058 PEG N760c Memorystick product SONY CLIE_40 0x0066 Sony Clie v4.0 product SONY MS_MSC_U03 0x0069 Memorystick MSC-U03 product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot product SONY CLIE_S360 0x0095 Sony Clie s360 product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot product SONY CLIE_41 0x009a Sony Clie v4.1 product SONY CLIE_NX60 0x00da Sony Clie nx60 product SONY CLIE_TH55 0x0144 Sony Clie th55 product SONY CLIE_TJ37 0x0169 Sony Clie tj37 product SONY RF_RECEIVER 0x01db Sony RF mouse/kbd Receiver VGP-WRC1 product SONY QN3 0x0437 Sony QN3 CMD-Jxx phone cable /* Sony Ericsson products */ product SONYERICSSON DCU10 0x0528 DCU-10 Phone Data Cable product SONYERICSSON DATAPILOT 0x2003 Datapilot Phone Cable /* SOURCENEXT products */ product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8 product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger /* SparkLAN products */ product SPARKLAN RT2573 0x0004 RT2573 product SPARKLAN RT2870_1 0x0006 RT2870 product SPARKLAN RT3070 0x0010 RT3070 /* Soundgraph products */ product SOUNDGRAPH IMON_VFD 0x0044 Antec Veris Elite VFD Panel, Knob, and Remote product SOUNDGRAPH SSTONE_LC16 0xffdc Silverstone LC16 VFD Panel, Knob, and Remote /* Speed Dragon Multimedia products */ product SPEEDDRAGON MS3303H 0x110b MS3303H Serial /* Sphairon Access Systems GmbH products */ product SPHAIRON UB801R 0x0110 UB801R /* Stelera Wireless products */ product STELERA ZEROCD 0x1000 Zerocd Installer product STELERA C105 0x1002 Stelera/Bandrish C105 USB product STELERA E1003 0x1003 3G modem product STELERA E1004 0x1004 3G modem product STELERA E1005 0x1005 3G modem product STELERA E1006 0x1006 3G modem product STELERA E1007 0x1007 3G modem product STELERA E1008 0x1008 3G modem product STELERA E1009 0x1009 3G modem product STELERA E100A 0x100a 3G modem product STELERA E100B 0x100b 3G modem product STELERA E100C 0x100c 3G modem product STELERA E100D 0x100d 3G modem product STELERA E100E 0x100e 3G modem product STELERA E100F 0x100f 3G modem product STELERA E1010 0x1010 3G modem product STELERA E1011 0x1011 3G modem product STELERA E1012 0x1012 3G modem /* STMicroelectronics products */ product STMICRO BIOCPU 0x2016 Biometric Coprocessor product STMICRO COMMUNICATOR 0x7554 USB Communicator product STMICRO ST72682 0xfada USB 2.0 Flash drive controller /* STSN products */ product STSN STSN0001 0x0001 Internet Access Device /* SUN Corporation products */ product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2 product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1 product SUNTAC VS10U 0x0009 SUNTAC Slipper U product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3 product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4 /* Sun Microsystems products */ product SUN KEYBOARD_TYPE_6 0x0005 Type 6 USB keyboard product SUN KEYBOARD_TYPE_7 0x00a2 Type 7 USB keyboard /* XXX The above is a North American PC style keyboard possibly */ product SUN MOUSE 0x0100 Type 6 USB mouse product SUN KBD_HUB 0x100e Kbd Hub /* Sunplus Innovation Technology Inc. products */ product SUNPLUS USBMOUSE 0x0007 USB Optical Mouse /* Super Top products */ product SUPERTOP IDE 0x6600 USB-IDE product SUPERTOP FLASHDRIVE 0x121c extrememory Snippy /* Syntech products */ product SYNTECH CPT8001C 0x0001 CPT-8001C Barcode scanner product SYNTECH CYPHERLAB100 0x1000 CipherLab USB Barcode Scanner /* Teclast products */ product TECLAST TLC300 0x3203 USB Media Player /* Testo products */ product TESTO USB_INTERFACE 0x0001 FTDI compatible adapter /* TexTech products */ product TEXTECH DUMMY 0x0000 Dummy product product TEXTECH U2M_1 0x0101 Textech USB MIDI cable product TEXTECH U2M_2 0x1806 Textech USB MIDI cable /* The Mobility Lab products */ product TML USB_SERIAL 0x0064 FTDI compatible adapter /* Thurlby Thandar Instrument products */ product TTI QL355P 0x03e8 FTDI compatible adapter /* Supra products */ product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem product DIAMOND2 RIO600USB 0x5001 Rio 600 USB product DIAMOND2 RIO800USB 0x5002 Rio 800 USB /* Surecom Technology products */ product SURECOM EP9001G2A 0x11f2 EP-9001-G rev 2A product SURECOM RT2570 0x11f3 RT2570 product SURECOM RT2573 0x31f3 RT2573 /* Sweex products */ product SWEEX ZD1211 0x1809 ZD1211 product SWEEX2 LW153 0x0153 LW153 product SWEEX2 LW154 0x0154 LW154 product SWEEX2 LW303 0x0302 LW303 product SWEEX2 LW313 0x0313 LW313 /* System TALKS, Inc. */ product SYSTEMTALKS SGCX2UL 0x1920 SGC-X2UL /* Tapwave products */ product TAPWAVE ZODIAC 0x0100 Zodiac /* Taugagreining products */ product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB) /* TCTMobile products */ product TCTMOBILE X060S 0x0000 X060S 3G modem product TCTMOBILE X080S 0xf000 X080S 3G modem /* TDK products */ product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664 product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464 product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400 product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400 product TDK BT_DONGLE 0x0309 Bluetooth USB dongle /* TEAC products */ product TEAC FD05PUB 0x0000 FD-05PUB floppy /* Tekram Technology products */ product TEKRAM QUICKWLAN 0x1630 QuickWLAN product TEKRAM ZD1211_1 0x5630 ZD1211 product TEKRAM ZD1211_2 0x6630 ZD1211 /* Telex Communications products */ product TELEX MIC1 0x0001 Enhanced USB Microphone /* Telit products */ product TELIT UC864E 0x1003 UC864E 3G modem product TELIT UC864G 0x1004 UC864G 3G modem /* Ten X Technology, Inc. */ product TENX UAUDIO0 0xf211 USB audio headset /* ThingM products */ product THINGM BLINK1 0x01ed USB notification light /* Texas Intel products */ product TI UTUSB41 0x1446 UT-USB41 hub product TI TUSB2046 0x2046 TUSB2046 hub product TI USB3410 0x3410 TI USB 3410 Modem product TI USB5052 0x5052 TI USB 5250 Modem product TI FRI2 0x5053 TI Fish River Island II product TI USB5052_EEPROM 0x505a TI USB 5250 Modem w/Eeprom product TI USB5052_FW 0x505f TI USB 5250 Modme w/Firmware running product TI USB5152 0x5152 TI USB 5152 Modem product TI EZ430 0xf430 TI ex430 development tool /* Thrustmaster products */ product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad /* TLayTech products */ product TLAYTECH TEU800 0x1682 TEU800 3G modem /* Topre Corporation products */ product TOPRE HHKB 0x0100 HHKB Professional /* Toshiba Corporation products */ product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740 product TOSHIBA RT3070 0x0a07 RT3070 product TOSHIBA G450 0x0d45 G450 modem product TOSHIBA HSDPA 0x1302 G450 modem product TOSHIBA TRANSMEMORY 0x6545 USB ThumbDrive /* TP-Link products */ product TPLINK T4U 0x0101 Archer T4U product TPLINK WN822NV4 0x0108 TL-WN822N v4 product TPLINK WN823NV2 0x0109 TL-WN823N v2 +product TPLINK WN722N 0x010c TL-WN722N product TPLINK T4UV2 0x010d Archer T4U ver 2 product TPLINK T4UHV2 0x010e Archer T4UH ver 2 product TPLINK RTL8153 0x0601 RTL8153 USB 10/100/1000 LAN /* Trek Technology products */ product TREK THUMBDRIVE 0x1111 ThumbDrive product TREK MEMKEY 0x8888 IBM USB Memory Key product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB /* TRENDnet products */ product TRENDNET RTL8192CU 0x624d RTL8192CU product TRENDNET TEW646UBH 0x646b TEW-646UBH product TRENDNET RTL8188CU 0x648b RTL8188CU product TRENDNET TEW805UB 0x805b TEW-805UB /* Tripp-Lite products */ product TRIPPLITE U209 0x2008 Serial /* Trumpion products */ product TRUMPION T33520 0x1001 T33520 USB Flash Card Controller product TRUMPION C3310 0x1100 Comotron C3310 MP3 player product TRUMPION MP3 0x1200 MP3 player /* TwinMOS */ product TWINMOS G240 0xa006 G240 product TWINMOS MDIV 0x1325 Memory Disk IV /* Ubiquam products */ product UBIQUAM UALL 0x3100 CDMA 1xRTT USB Modem (U-100/105/200/300/520) /* Ultima products */ product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner /* UMAX products */ product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner product UMAX ASTRA3400 0x0060 Astra 3400 Scanner /* U-MEDIA Communications products */ product UMEDIA TEW444UBEU 0x3006 TEW-444UB EU product UMEDIA TEW444UBEU_NF 0x3007 TEW-444UB EU (no firmware) product UMEDIA TEW429UB_A 0x300a TEW-429UB_A product UMEDIA TEW429UB 0x300b TEW-429UB product UMEDIA TEW429UBC1 0x300d TEW-429UB C1 product UMEDIA RT2870_1 0x300e RT2870 product UMEDIA ALL0298V2 0x3204 ALL0298 v2 product UMEDIA AR5523_2 0x3205 AR5523 product UMEDIA AR5523_2_NF 0x3206 AR5523 (no firmware) /* Universal Access products */ product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter /* Unknown products */ product UNKNOWN4 NF_RIC 0x0001 FTDI compatible adapter /* USI products */ product USI MC60 0x10c5 MC60 Serial /* U.S. Robotics products */ product USR USR5422 0x0118 USR5422 WLAN product USR USR5423 0x0121 USR5423 WLAN /* VIA Technologies products */ product VIA USB2IDEBRIDGE 0x6204 USB 2.0 IDE Bridge /* VIA Labs */ product VIALABS USB30SATABRIDGE 0x0700 USB 3.0 SATA Bridge /* Vaisala products */ product VAISALA CABLE 0x0200 USB Interface cable /* Vertex products */ product VERTEX VW110L 0x0100 Vertex VW110L modem /* VidzMedia products */ product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H /* Vision products */ product VISION VC6452V002 0x0002 CPiA Camera /* Visioneer products */ product VISIONEER 7600 0x0211 OneTouch 7600 product VISIONEER 5300 0x0221 OneTouch 5300 product VISIONEER 3000 0x0224 Scanport 3000 product VISIONEER 6100 0x0231 OneTouch 6100 product VISIONEER 6200 0x0311 OneTouch 6200 product VISIONEER 8100 0x0321 OneTouch 8100 product VISIONEER 8600 0x0331 OneTouch 8600 /* Vivitar products */ product VIVITAR 35XX 0x0003 Vivicam 35Xx /* VTech products */ product VTECH RT2570 0x3012 RT2570 product VTECH ZD1211B 0x3014 ZD1211B /* Wacom products */ product WACOM CT0405U 0x0000 CT-0405-U Tablet product WACOM GRAPHIRE 0x0010 Graphire product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5 product WACOM INTUOSA5 0x0021 Intuos A5 product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet /* WAGO Kontakttechnik GmbH products */ product WAGO SERVICECABLE 0x07a6 USB Service Cable 750-923 /* WaveSense products */ product WAVESENSE JAZZ 0xaaaa Jazz blood glucose meter /* WCH products */ product WCH CH341SER 0x5523 CH341/CH340 USB-Serial Bridge product WCH2 DUMMY 0x0000 Dummy product product WCH2 CH341SER_2 0x5523 CH341/CH340 USB-Serial Bridge product WCH2 CH341SER 0x7523 CH341/CH340 USB-Serial Bridge product WCH2 U2M 0X752d CH345 USB2.0-MIDI /* West Mountain Radio products */ product WESTMOUNTAIN RIGBLASTER_ADVANTAGE 0x0003 RIGblaster Advantage /* Western Digital products */ product WESTERN COMBO 0x0200 Firewire USB Combo product WESTERN EXTHDD 0x0400 External HDD product WESTERN HUB 0x0500 USB HUB product WESTERN MYBOOK 0x0901 MyBook External HDD product WESTERN MYPASSPORT_00 0x0704 MyPassport External HDD product WESTERN MYPASSPORT_11 0x0741 MyPassport External HDD product WESTERN MYPASSPORT_01 0x0746 MyPassport External HDD product WESTERN MYPASSPORT_02 0x0748 MyPassport External HDD product WESTERN MYPASSPORT_03 0x074A MyPassport External HDD product WESTERN MYPASSPORT_04 0x074C MyPassport External HDD product WESTERN MYPASSPORT_05 0x074E MyPassport External HDD product WESTERN MYPASSPORT_06 0x07A6 MyPassport External HDD product WESTERN MYPASSPORT_07 0x07A8 MyPassport External HDD product WESTERN MYPASSPORT_08 0x07AA MyPassport External HDD product WESTERN MYPASSPORT_09 0x07AC MyPassport External HDD product WESTERN MYPASSPORT_10 0x07AE MyPassport External HDD product WESTERN MYPASSPORTES_00 0x070A MyPassport Essential External HDD product WESTERN MYPASSPORTES_01 0x071A MyPassport Essential External HDD product WESTERN MYPASSPORTES_02 0x0730 MyPassport Essential External HDD product WESTERN MYPASSPORTES_03 0x0732 MyPassport Essential External HDD product WESTERN MYPASSPORTES_04 0x0740 MyPassport Essential External HDD product WESTERN MYPASSPORTES_05 0x0742 MyPassport Essential External HDD product WESTERN MYPASSPORTES_06 0x0750 MyPassport Essential External HDD product WESTERN MYPASSPORTES_07 0x0752 MyPassport Essential External HDD product WESTERN MYPASSPORTES_08 0x07A0 MyPassport Essential External HDD product WESTERN MYPASSPORTES_09 0x07A2 MyPassport Essential External HDD /* WeTelecom products */ product WETELECOM WM_D200 0x6801 WM-D200 /* WIENER Plein & Baus GmbH products */ product WIENERPLEINBAUS PL512 0x0010 PL512 PSU product WIENERPLEINBAUS RCM 0x0011 RCM Remote Control product WIENERPLEINBAUS MPOD 0x0012 MPOD PSU product WIENERPLEINBAUS CML 0x0015 CML Data Logger /* Windbond Electronics */ product WINBOND UH104 0x5518 4-port USB Hub /* WinMaxGroup products */ product WINMAXGROUP FLASH64MC 0x6660 USB Flash Disk 64M-C /* Wistron NeWeb products */ product WISTRONNEWEB WNC0600 0x0326 WNC-0600USB product WISTRONNEWEB UR045G 0x0427 PrismGT USB 2.0 WLAN product WISTRONNEWEB UR055G 0x0711 UR055G product WISTRONNEWEB O8494 0x0804 ORiNOCO 802.11n product WISTRONNEWEB AR5523_1 0x0826 AR5523 product WISTRONNEWEB AR5523_1_NF 0x0827 AR5523 (no firmware) product WISTRONNEWEB AR5523_2 0x082a AR5523 product WISTRONNEWEB AR5523_2_NF 0x0829 AR5523 (no firmware) /* Xerox products */ product XEROX WCM15 0xffef WorkCenter M15 /* Xirlink products */ product XIRLINK PCCAM 0x8080 IBM PC Camera /* Xyratex products */ product XYRATEX PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN product XYRATEX PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN /* Yamaha products */ product YAMAHA UX256 0x1000 UX256 MIDI I/F product YAMAHA MU1000 0x1001 MU1000 MIDI Synth. product YAMAHA MU2000 0x1002 MU2000 MIDI Synth. product YAMAHA MU500 0x1003 MU500 MIDI Synth. product YAMAHA UW500 0x1004 UW500 USB Audio I/F product YAMAHA MOTIF6 0x1005 MOTIF6 MIDI Synth. Workstation product YAMAHA MOTIF7 0x1006 MOTIF7 MIDI Synth. Workstation product YAMAHA MOTIF8 0x1007 MOTIF8 MIDI Synth. Workstation product YAMAHA UX96 0x1008 UX96 MIDI I/F product YAMAHA UX16 0x1009 UX16 MIDI I/F product YAMAHA S08 0x100e S08 MIDI Keyboard product YAMAHA CLP150 0x100f CLP-150 digital piano product YAMAHA CLP170 0x1010 CLP-170 digital piano product YAMAHA RPU200 0x3104 RP-U200 product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router /* Yano products */ product YANO U640MO 0x0101 U640MO-03 product YANO FW800HD 0x05fc METALWEAR-HDD /* Y.C. Cable products */ product YCCABLE PL2303 0x0fba PL2303 Serial /* Y-E Data products */ product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U /* Yiso Wireless Co. products */ product YISO C893 0xc893 CDMA 2000 1xEVDO PC Card /* Z-Com products */ product ZCOM M4Y750 0x0001 M4Y-750 product ZCOM XI725 0x0002 XI-725/726 product ZCOM XI735 0x0005 XI-735 product ZCOM XG703A 0x0008 PrismGT USB 2.0 WLAN product ZCOM ZD1211 0x0011 ZD1211 product ZCOM AR5523 0x0012 AR5523 product ZCOM AR5523_NF 0x0013 AR5523 driver (no firmware) product ZCOM XM142 0x0015 XM-142 product ZCOM ZD1211B 0x001a ZD1211B product ZCOM RT2870_1 0x0022 RT2870 product ZCOM UB81 0x0023 UB81 product ZCOM RT2870_2 0x0025 RT2870 product ZCOM UB82 0x0026 UB82 /* Zeevo, Inc. products */ product ZEEVO BLUETOOTH 0x07d0 BT-500 Bluetooth USB Adapter /* Zinwell products */ product ZINWELL RT2570 0x0260 RT2570 product ZINWELL RT2870_1 0x0280 RT2870 product ZINWELL RT2870_2 0x0282 RT2870 product ZINWELL RT3072_1 0x0283 RT3072 product ZINWELL RT3072_2 0x0284 RT3072 product ZINWELL RT3070 0x5257 RT3070 /* Zoom Telephonics, Inc. products */ product ZOOM 2986L 0x9700 2986L Fax modem product ZOOM 3095 0x3095 3095 USB Fax modem /* Zoran Microelectronics products */ product ZORAN EX20DSC 0x4343 Digital Camera EX-20 DSC /* ZTE products */ product ZTE MF622 0x0001 MF622 modem product ZTE MF628 0x0015 MF628 modem product ZTE MF626 0x0031 MF626 modem product ZTE MF820D_INSTALLER 0x0166 MF820D CD product ZTE MF820D 0x0167 MF820D modem product ZTE INSTALLER 0x2000 UMTS CD product ZTE MC2718 0xffe8 MC2718 modem product ZTE AC8700 0xfffe CDMA 1xEVDO USB modem /* Zydas Technology Corporation products */ product ZYDAS ZD1201 0x1201 ZD1201 product ZYDAS ZD1211 0x1211 ZD1211 WLAN abg product ZYDAS ZD1211B 0x1215 ZD1211B product ZYDAS ZD1221 0x1221 ZD1221 product ZYDAS ALL0298 0xa211 ALL0298 product ZYDAS ZD1211B_2 0xb215 ZD1211B /* ZyXEL Communication Co. products */ product ZYXEL OMNI56K 0x1500 Omni 56K Plus product ZYXEL 980N 0x2011 Scorpion-980N keyboard product ZYXEL ZYAIRG220 0x3401 ZyAIR G-220 product ZYXEL G200V2 0x3407 G-200 v2 product ZYXEL AG225H 0x3409 AG-225H product ZYXEL M202 0x340a M-202 product ZYXEL G270S 0x340c G-270S product ZYXEL G220V2 0x340f G-220 v2 product ZYXEL G202 0x3410 G-202 product ZYXEL RT2573 0x3415 RT2573 product ZYXEL RT2870_1 0x3416 RT2870 product ZYXEL NWD271N 0x3417 NWD-271N product ZYXEL NWD211AN 0x3418 NWD-211AN product ZYXEL RT2870_2 0x341a RT2870 product ZYXEL RT3070 0x341e NWD2105 product ZYXEL RTL8192CU 0x341f RTL8192CU product ZYXEL NWD2705 0x3421 NWD2705 product ZYXEL NWD6605 0x3426 ND6605 product ZYXEL PRESTIGE 0x401a Prestige Index: projects/clang700-import/sys/dev/usb/wlan/if_rsu.c =================================================================== --- projects/clang700-import/sys/dev/usb/wlan/if_rsu.c (revision 341812) +++ projects/clang700-import/sys/dev/usb/wlan/if_rsu.c (revision 341813) @@ -1,3742 +1,3743 @@ /* $OpenBSD: if_rsu.c,v 1.17 2013/04/15 09:23:01 mglocker Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU. * * TODO: * o tx a-mpdu * o hostap / ibss / mesh * o power-save operation */ #include "opt_wlan.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 "usbdevs.h" #include /* XXX */ #include #define RSU_RATE_IS_CCK RTWN_RATE_IS_CCK #ifdef USB_DEBUG static int rsu_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu"); SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RWTUN, &rsu_debug, 0, "Debug level"); #define RSU_DPRINTF(_sc, _flg, ...) \ do \ if (((_flg) == (RSU_DEBUG_ANY)) || (rsu_debug & (_flg))) \ device_printf((_sc)->sc_dev, __VA_ARGS__); \ while (0) #else #define RSU_DPRINTF(_sc, _flg, ...) #endif static int rsu_enable_11n = 1; TUNABLE_INT("hw.usb.rsu.enable_11n", &rsu_enable_11n); #define RSU_DEBUG_ANY 0xffffffff #define RSU_DEBUG_TX 0x00000001 #define RSU_DEBUG_RX 0x00000002 #define RSU_DEBUG_RESET 0x00000004 #define RSU_DEBUG_CALIB 0x00000008 #define RSU_DEBUG_STATE 0x00000010 #define RSU_DEBUG_SCAN 0x00000020 #define RSU_DEBUG_FWCMD 0x00000040 #define RSU_DEBUG_TXDONE 0x00000080 #define RSU_DEBUG_FW 0x00000100 #define RSU_DEBUG_FWDBG 0x00000200 #define RSU_DEBUG_AMPDU 0x00000400 #define RSU_DEBUG_KEY 0x00000800 #define RSU_DEBUG_USB 0x00001000 static const STRUCT_USB_HOST_ID rsu_devs[] = { #define RSU_HT_NOT_SUPPORTED 0 #define RSU_HT_SUPPORTED 1 #define RSU_DEV_HT(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \ RSU_HT_SUPPORTED) } #define RSU_DEV(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \ RSU_HT_NOT_SUPPORTED) } RSU_DEV(ASUS, RTL8192SU), RSU_DEV(AZUREWAVE, RTL8192SU_4), + RSU_DEV(SITECOMEU, WLA1000), RSU_DEV_HT(ACCTON, RTL8192SU), RSU_DEV_HT(ASUS, USBN10), RSU_DEV_HT(AZUREWAVE, RTL8192SU_1), RSU_DEV_HT(AZUREWAVE, RTL8192SU_2), RSU_DEV_HT(AZUREWAVE, RTL8192SU_3), RSU_DEV_HT(AZUREWAVE, RTL8192SU_5), RSU_DEV_HT(BELKIN, RTL8192SU_1), RSU_DEV_HT(BELKIN, RTL8192SU_2), RSU_DEV_HT(BELKIN, RTL8192SU_3), RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_1), RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_2), RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_3), RSU_DEV_HT(COREGA, RTL8192SU), RSU_DEV_HT(DLINK2, DWA131A1), RSU_DEV_HT(DLINK2, RTL8192SU_1), RSU_DEV_HT(DLINK2, RTL8192SU_2), RSU_DEV_HT(EDIMAX, RTL8192SU_1), RSU_DEV_HT(EDIMAX, RTL8192SU_2), RSU_DEV_HT(EDIMAX, EW7622UMN), RSU_DEV_HT(GUILLEMOT, HWGUN54), RSU_DEV_HT(GUILLEMOT, HWNUM300), RSU_DEV_HT(HAWKING, RTL8192SU_1), RSU_DEV_HT(HAWKING, RTL8192SU_2), RSU_DEV_HT(PLANEX2, GWUSNANO), RSU_DEV_HT(REALTEK, RTL8171), RSU_DEV_HT(REALTEK, RTL8172), RSU_DEV_HT(REALTEK, RTL8173), RSU_DEV_HT(REALTEK, RTL8174), RSU_DEV_HT(REALTEK, RTL8192SU), RSU_DEV_HT(REALTEK, RTL8712), RSU_DEV_HT(REALTEK, RTL8713), RSU_DEV_HT(SENAO, RTL8192SU_1), RSU_DEV_HT(SENAO, RTL8192SU_2), RSU_DEV_HT(SITECOMEU, WL349V1), RSU_DEV_HT(SITECOMEU, WL353), RSU_DEV_HT(SWEEX2, LW154), RSU_DEV_HT(TRENDNET, TEW646UBH), #undef RSU_DEV_HT #undef RSU_DEV }; static device_probe_t rsu_match; static device_attach_t rsu_attach; static device_detach_t rsu_detach; static usb_callback_t rsu_bulk_tx_callback_be_bk; static usb_callback_t rsu_bulk_tx_callback_vi_vo; static usb_callback_t rsu_bulk_tx_callback_h2c; static usb_callback_t rsu_bulk_rx_callback; static usb_error_t rsu_do_request(struct rsu_softc *, struct usb_device_request *, void *); static struct ieee80211vap * rsu_vap_create(struct ieee80211com *, const char name[], int, enum ieee80211_opmode, int, const uint8_t bssid[], const uint8_t mac[]); static void rsu_vap_delete(struct ieee80211vap *); static void rsu_scan_start(struct ieee80211com *); static void rsu_scan_end(struct ieee80211com *); static void rsu_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static void rsu_set_channel(struct ieee80211com *); static void rsu_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void rsu_scan_mindwell(struct ieee80211_scan_state *); static void rsu_update_promisc(struct ieee80211com *); static uint8_t rsu_get_multi_pos(const uint8_t[]); static void rsu_set_multi(struct rsu_softc *); static void rsu_update_mcast(struct ieee80211com *); static int rsu_alloc_rx_list(struct rsu_softc *); static void rsu_free_rx_list(struct rsu_softc *); static int rsu_alloc_tx_list(struct rsu_softc *); static void rsu_free_tx_list(struct rsu_softc *); static void rsu_free_list(struct rsu_softc *, struct rsu_data [], int); static struct rsu_data *_rsu_getbuf(struct rsu_softc *); static struct rsu_data *rsu_getbuf(struct rsu_softc *); static void rsu_freebuf(struct rsu_softc *, struct rsu_data *); static int rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *, int); static void rsu_write_1(struct rsu_softc *, uint16_t, uint8_t); static void rsu_write_2(struct rsu_softc *, uint16_t, uint16_t); static void rsu_write_4(struct rsu_softc *, uint16_t, uint32_t); static int rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *, int); static uint8_t rsu_read_1(struct rsu_softc *, uint16_t); static uint16_t rsu_read_2(struct rsu_softc *, uint16_t); static uint32_t rsu_read_4(struct rsu_softc *, uint16_t); static int rsu_fw_iocmd(struct rsu_softc *, uint32_t); static uint8_t rsu_efuse_read_1(struct rsu_softc *, uint16_t); static int rsu_read_rom(struct rsu_softc *); static int rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int); static void rsu_calib_task(void *, int); static void rsu_tx_task(void *, int); static void rsu_set_led(struct rsu_softc *, int); static int rsu_monitor_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int rsu_key_alloc(struct ieee80211vap *, struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); static int rsu_process_key(struct ieee80211vap *, const struct ieee80211_key *, int); static int rsu_key_set(struct ieee80211vap *, const struct ieee80211_key *); static int rsu_key_delete(struct ieee80211vap *, const struct ieee80211_key *); static int rsu_cam_read(struct rsu_softc *, uint8_t, uint32_t *); static void rsu_cam_write(struct rsu_softc *, uint8_t, uint32_t); static int rsu_key_check(struct rsu_softc *, ieee80211_keyix, int); static uint8_t rsu_crypto_mode(struct rsu_softc *, u_int, int); static int rsu_set_key_group(struct rsu_softc *, const struct ieee80211_key *); static int rsu_set_key_pair(struct rsu_softc *, const struct ieee80211_key *); static int rsu_reinit_static_keys(struct rsu_softc *); static int rsu_delete_key(struct rsu_softc *sc, ieee80211_keyix); static void rsu_delete_key_pair_cb(void *, int); static int rsu_site_survey(struct rsu_softc *, struct ieee80211_scan_ssid *); static int rsu_join_bss(struct rsu_softc *, struct ieee80211_node *); static int rsu_disconnect(struct rsu_softc *); static int rsu_hwrssi_to_rssi(struct rsu_softc *, int hw_rssi); static void rsu_event_survey(struct rsu_softc *, uint8_t *, int); static void rsu_event_join_bss(struct rsu_softc *, uint8_t *, int); static void rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int); static void rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int); static int8_t rsu_get_rssi(struct rsu_softc *, int, void *); static struct mbuf * rsu_rx_copy_to_mbuf(struct rsu_softc *, struct r92s_rx_stat *, int); static uint32_t rsu_get_tsf_low(struct rsu_softc *); static uint32_t rsu_get_tsf_high(struct rsu_softc *); static struct ieee80211_node * rsu_rx_frame(struct rsu_softc *, struct mbuf *); static struct mbuf * rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int); static struct mbuf * rsu_rxeof(struct usb_xfer *, struct rsu_data *); static void rsu_txeof(struct usb_xfer *, struct rsu_data *); static int rsu_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void rsu_rxfilter_init(struct rsu_softc *); static void rsu_rxfilter_set(struct rsu_softc *, uint32_t, uint32_t); static void rsu_rxfilter_refresh(struct rsu_softc *); static int rsu_init(struct rsu_softc *); static int rsu_tx_start(struct rsu_softc *, struct ieee80211_node *, struct mbuf *, struct rsu_data *); static int rsu_transmit(struct ieee80211com *, struct mbuf *); static void rsu_start(struct rsu_softc *); static void _rsu_start(struct rsu_softc *); static int rsu_ioctl_net(struct ieee80211com *, u_long, void *); static void rsu_parent(struct ieee80211com *); static void rsu_stop(struct rsu_softc *); static void rsu_ms_delay(struct rsu_softc *, int); static device_method_t rsu_methods[] = { DEVMETHOD(device_probe, rsu_match), DEVMETHOD(device_attach, rsu_attach), DEVMETHOD(device_detach, rsu_detach), DEVMETHOD_END }; static driver_t rsu_driver = { .name = "rsu", .methods = rsu_methods, .size = sizeof(struct rsu_softc) }; static devclass_t rsu_devclass; DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0); MODULE_DEPEND(rsu, wlan, 1, 1, 1); MODULE_DEPEND(rsu, usb, 1, 1, 1); MODULE_DEPEND(rsu, firmware, 1, 1, 1); MODULE_VERSION(rsu, 1); USB_PNP_HOST_INFO(rsu_devs); static const uint8_t rsu_chan_2ghz[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; static uint8_t rsu_wme_ac_xfer_map[4] = { [WME_AC_BE] = RSU_BULK_TX_BE_BK, [WME_AC_BK] = RSU_BULK_TX_BE_BK, [WME_AC_VI] = RSU_BULK_TX_VI_VO, [WME_AC_VO] = RSU_BULK_TX_VI_VO, }; /* XXX hard-coded */ #define RSU_H2C_ENDPOINT 3 static const struct usb_config rsu_config[RSU_N_TRANSFER] = { [RSU_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = RSU_RXBUFSZ, .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = rsu_bulk_rx_callback }, [RSU_BULK_TX_BE_BK] = { .type = UE_BULK, .endpoint = 0x06, .direction = UE_DIR_OUT, .bufsize = RSU_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .force_short_xfer = 1 }, .callback = rsu_bulk_tx_callback_be_bk, .timeout = RSU_TX_TIMEOUT }, [RSU_BULK_TX_VI_VO] = { .type = UE_BULK, .endpoint = 0x04, .direction = UE_DIR_OUT, .bufsize = RSU_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .force_short_xfer = 1 }, .callback = rsu_bulk_tx_callback_vi_vo, .timeout = RSU_TX_TIMEOUT }, [RSU_BULK_TX_H2C] = { .type = UE_BULK, .endpoint = 0x0d, .direction = UE_DIR_OUT, .bufsize = RSU_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = rsu_bulk_tx_callback_h2c, .timeout = RSU_TX_TIMEOUT }, }; static int rsu_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST || uaa->info.bIfaceIndex != 0 || uaa->info.bConfigIndex != 0) return (ENXIO); return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa)); } static int rsu_send_mgmt(struct ieee80211_node *ni, int type, int arg) { return (ENOTSUP); } static void rsu_update_chw(struct ieee80211com *ic) { } /* * notification from net80211 that it'd like to do A-MPDU on the given TID. * * Note: this actually hangs traffic at the present moment, so don't use it. * The firmware debug does indiciate it's sending and establishing a TX AMPDU * session, but then no traffic flows. */ static int rsu_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { #if 0 struct rsu_softc *sc = ni->ni_ic->ic_softc; struct r92s_add_ba_req req; /* Don't enable if it's requested or running */ if (IEEE80211_AMPDU_REQUESTED(tap)) return (0); if (IEEE80211_AMPDU_RUNNING(tap)) return (0); /* We've decided to send addba; so send it */ req.tid = htole32(tap->txa_tid); /* Attempt net80211 state */ if (ieee80211_ampdu_tx_request_ext(ni, tap->txa_tid) != 1) return (0); /* Send the firmware command */ RSU_DPRINTF(sc, RSU_DEBUG_AMPDU, "%s: establishing AMPDU TX for TID %d\n", __func__, tap->txa_tid); RSU_LOCK(sc); if (rsu_fw_cmd(sc, R92S_CMD_ADDBA_REQ, &req, sizeof(req)) != 1) { RSU_UNLOCK(sc); /* Mark failure */ (void) ieee80211_ampdu_tx_request_active_ext(ni, tap->txa_tid, 0); return (0); } RSU_UNLOCK(sc); /* Mark success; we don't get any further notifications */ (void) ieee80211_ampdu_tx_request_active_ext(ni, tap->txa_tid, 1); #endif /* Return 0, we're driving this ourselves */ return (0); } static int rsu_wme_update(struct ieee80211com *ic) { /* Firmware handles this; not our problem */ return (0); } static int rsu_attach(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); struct rsu_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; int error; uint8_t iface_index; struct usb_interface *iface; const char *rft; device_set_usb_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; sc->sc_rx_checksum_enable = 1; if (rsu_enable_11n) sc->sc_ht = !! (USB_GET_DRIVER_INFO(uaa) & RSU_HT_SUPPORTED); /* Get number of endpoints */ iface = usbd_get_iface(sc->sc_udev, 0); sc->sc_nendpoints = iface->idesc->bNumEndpoints; /* Endpoints are hard-coded for now, so enforce 4-endpoint only */ if (sc->sc_nendpoints != 4) { device_printf(sc->sc_dev, "the driver currently only supports 4-endpoint devices\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF); RSU_DELKEY_BMAP_LOCK_INIT(sc); TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0, rsu_calib_task, sc); TASK_INIT(&sc->del_key_task, 0, rsu_delete_key_pair_cb, sc); TASK_INIT(&sc->tx_task, 0, rsu_tx_task, sc); mbufq_init(&sc->sc_snd, ifqmaxlen); /* Allocate Tx/Rx buffers. */ error = rsu_alloc_rx_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Rx buffers\n"); goto fail_usb; } error = rsu_alloc_tx_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx buffers\n"); rsu_free_rx_list(sc); goto fail_usb; } iface_index = 0; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(sc->sc_dev, "could not allocate USB transfers, err=%s\n", usbd_errstr(error)); goto fail_usb; } RSU_LOCK(sc); /* Read chip revision. */ sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT); if (sc->cut != 3) sc->cut = (sc->cut >> 1) + 1; error = rsu_read_rom(sc); RSU_UNLOCK(sc); if (error != 0) { device_printf(self, "could not read ROM\n"); goto fail_rom; } /* Figure out TX/RX streams */ switch (sc->rom[84]) { case 0x0: sc->sc_rftype = RTL8712_RFCONFIG_1T1R; sc->sc_nrxstream = 1; sc->sc_ntxstream = 1; rft = "1T1R"; break; case 0x1: sc->sc_rftype = RTL8712_RFCONFIG_1T2R; sc->sc_nrxstream = 2; sc->sc_ntxstream = 1; rft = "1T2R"; break; case 0x2: sc->sc_rftype = RTL8712_RFCONFIG_2T2R; sc->sc_nrxstream = 2; sc->sc_ntxstream = 2; rft = "2T2R"; break; case 0x3: /* "green" NIC */ sc->sc_rftype = RTL8712_RFCONFIG_1T2R; sc->sc_nrxstream = 2; sc->sc_ntxstream = 1; rft = "1T2R ('green')"; break; default: device_printf(sc->sc_dev, "%s: unknown board type (rfconfig=0x%02x)\n", __func__, sc->rom[84]); goto fail_rom; } IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->rom[0x12]); device_printf(self, "MAC/BB RTL8712 cut %d %s\n", sc->cut, rft); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(self); ic->ic_phytype = IEEE80211_T_OFDM; /* Not only, but not used. */ ic->ic_opmode = IEEE80211_M_STA; /* Default to BSS mode. */ /* Set device capabilities. */ ic->ic_caps = IEEE80211_C_STA | /* station mode */ IEEE80211_C_MONITOR | /* monitor mode supported */ #if 0 IEEE80211_C_BGSCAN | /* Background scan. */ #endif IEEE80211_C_SHPREAMBLE | /* Short preamble supported. */ IEEE80211_C_WME | /* WME/QoS */ IEEE80211_C_SHSLOT | /* Short slot time supported. */ IEEE80211_C_WPA; /* WPA/RSN. */ ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_AES_CCM; /* Check if HT support is present. */ if (sc->sc_ht) { device_printf(sc->sc_dev, "%s: enabling 11n\n", __func__); /* Enable basic HT */ ic->ic_htcaps = IEEE80211_HTC_HT | #if 0 IEEE80211_HTC_AMPDU | #endif IEEE80211_HTC_AMSDU | IEEE80211_HTCAP_MAXAMSDU_3839 | IEEE80211_HTCAP_SMPS_OFF; ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40; /* set number of spatial streams */ ic->ic_txstream = sc->sc_ntxstream; ic->ic_rxstream = sc->sc_nrxstream; } ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD; rsu_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); ieee80211_ifattach(ic); ic->ic_raw_xmit = rsu_raw_xmit; ic->ic_scan_start = rsu_scan_start; ic->ic_scan_end = rsu_scan_end; ic->ic_getradiocaps = rsu_getradiocaps; ic->ic_set_channel = rsu_set_channel; ic->ic_scan_curchan = rsu_scan_curchan; ic->ic_scan_mindwell = rsu_scan_mindwell; ic->ic_vap_create = rsu_vap_create; ic->ic_vap_delete = rsu_vap_delete; ic->ic_update_promisc = rsu_update_promisc; ic->ic_update_mcast = rsu_update_mcast; ic->ic_ioctl = rsu_ioctl_net; ic->ic_parent = rsu_parent; ic->ic_transmit = rsu_transmit; ic->ic_send_mgmt = rsu_send_mgmt; ic->ic_update_chw = rsu_update_chw; ic->ic_ampdu_enable = rsu_ampdu_enable; ic->ic_wme.wme_update = rsu_wme_update; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RSU_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return (0); fail_rom: usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER); fail_usb: mtx_destroy(&sc->sc_mtx); return (ENXIO); } static int rsu_detach(device_t self) { struct rsu_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; rsu_stop(sc); usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER); /* * Free buffers /before/ we detach from net80211, else node * references to destroyed vaps will lead to a panic. */ /* Free Tx/Rx buffers. */ RSU_LOCK(sc); rsu_free_tx_list(sc); rsu_free_rx_list(sc); RSU_UNLOCK(sc); /* Frames are freed; detach from net80211 */ ieee80211_ifdetach(ic); taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task); taskqueue_drain(taskqueue_thread, &sc->del_key_task); taskqueue_drain(taskqueue_thread, &sc->tx_task); RSU_DELKEY_BMAP_LOCK_DESTROY(sc); mtx_destroy(&sc->sc_mtx); return (0); } static usb_error_t rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; RSU_ASSERT_LOCKED(sc); while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0 || err == USB_ERR_NOT_CONFIGURED) break; RSU_DPRINTF(sc, RSU_DEBUG_USB, "Control request failed, %s (retries left: %d)\n", usbd_errstr(err), ntries); rsu_ms_delay(sc, 10); } return (err); } static struct ieee80211vap * rsu_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct rsu_softc *sc = ic->ic_softc; struct rsu_vap *uvp; struct ieee80211vap *vap; struct ifnet *ifp; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); uvp = malloc(sizeof(struct rsu_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } ifp = vap->iv_ifp; ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; RSU_LOCK(sc); if (sc->sc_rx_checksum_enable) ifp->if_capenable |= IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; RSU_UNLOCK(sc); /* override state transition machine */ uvp->newstate = vap->iv_newstate; if (opmode == IEEE80211_M_MONITOR) vap->iv_newstate = rsu_monitor_newstate; else vap->iv_newstate = rsu_newstate; vap->iv_key_alloc = rsu_key_alloc; vap->iv_key_set = rsu_key_set; vap->iv_key_delete = rsu_key_delete; /* Limits from the r92su driver */ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16; vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void rsu_vap_delete(struct ieee80211vap *vap) { struct rsu_vap *uvp = RSU_VAP(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void rsu_scan_start(struct ieee80211com *ic) { struct rsu_softc *sc = ic->ic_softc; struct ieee80211_scan_state *ss = ic->ic_scan; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); int error; /* Scanning is done by the firmware. */ RSU_LOCK(sc); sc->sc_active_scan = !!(ss->ss_flags & IEEE80211_SCAN_ACTIVE); /* XXX TODO: force awake if in network-sleep? */ error = rsu_site_survey(sc, ss->ss_nssid > 0 ? &ss->ss_ssid[0] : NULL); RSU_UNLOCK(sc); if (error != 0) { device_printf(sc->sc_dev, "could not send site survey command\n"); ieee80211_cancel_scan(vap); } } static void rsu_scan_end(struct ieee80211com *ic) { /* Nothing to do here. */ } static void rsu_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { struct rsu_softc *sc = ic->ic_softc; uint8_t bands[IEEE80211_MODE_BYTES]; /* Set supported .11b and .11g rates. */ memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); if (sc->sc_ht) setbit(bands, IEEE80211_MODE_11NG); ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, rsu_chan_2ghz, nitems(rsu_chan_2ghz), bands, (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) != 0); } static void rsu_set_channel(struct ieee80211com *ic) { struct rsu_softc *sc = ic->ic_softc; /* * Only need to set the channel in Monitor mode. AP scanning and auth * are already taken care of by their respective firmware commands. */ if (ic->ic_opmode == IEEE80211_M_MONITOR) { struct r92s_set_channel cmd; int error; cmd.channel = IEEE80211_CHAN2IEEE(ic->ic_curchan); RSU_LOCK(sc); error = rsu_fw_cmd(sc, R92S_CMD_SET_CHANNEL, &cmd, sizeof(cmd)); if (error != 0) { device_printf(sc->sc_dev, "%s: error %d setting channel\n", __func__, error); } RSU_UNLOCK(sc); } } static void rsu_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { /* Scan is done in rsu_scan_start(). */ } /** * Called by the net80211 framework to indicate * the minimum dwell time has been met, terminate the scan. * We don't actually terminate the scan as the firmware will notify * us when it's finished and we have no way to interrupt it. */ static void rsu_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } static void rsu_update_promisc(struct ieee80211com *ic) { struct rsu_softc *sc = ic->ic_softc; RSU_LOCK(sc); if (sc->sc_running) rsu_rxfilter_refresh(sc); RSU_UNLOCK(sc); } /* * The same as rtwn_get_multi_pos() / rtwn_set_multi(). */ static uint8_t rsu_get_multi_pos(const uint8_t maddr[]) { uint64_t mask = 0x00004d101df481b4; uint8_t pos = 0x27; /* initial value */ int i, j; for (i = 0; i < IEEE80211_ADDR_LEN; i++) for (j = (i == 0) ? 1 : 0; j < 8; j++) if ((maddr[i] >> j) & 1) pos ^= (mask >> (i * 8 + j - 1)); pos &= 0x3f; return (pos); } static void rsu_set_multi(struct rsu_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t mfilt[2]; RSU_ASSERT_LOCKED(sc); /* general structure was copied from ath(4). */ if (ic->ic_allmulti == 0) { struct ieee80211vap *vap; struct ifnet *ifp; struct ifmultiaddr *ifma; /* * Merge multicast addresses to form the hardware filter. */ mfilt[0] = mfilt[1] = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { ifp = vap->iv_ifp; if_maddr_rlock(ifp); CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { caddr_t dl; uint8_t pos; dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); pos = rsu_get_multi_pos(dl); mfilt[pos / 32] |= (1 << (pos % 32)); } if_maddr_runlock(ifp); } } else mfilt[0] = mfilt[1] = ~0; rsu_write_4(sc, R92S_MAR + 0, mfilt[0]); rsu_write_4(sc, R92S_MAR + 4, mfilt[1]); RSU_DPRINTF(sc, RSU_DEBUG_STATE, "%s: MC filter %08x:%08x\n", __func__, mfilt[0], mfilt[1]); } static void rsu_update_mcast(struct ieee80211com *ic) { struct rsu_softc *sc = ic->ic_softc; RSU_LOCK(sc); if (sc->sc_running) rsu_set_multi(sc); RSU_UNLOCK(sc); } static int rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[], int ndata, int maxsz) { int i, error; for (i = 0; i < ndata; i++) { struct rsu_data *dp = &data[i]; dp->sc = sc; dp->m = NULL; dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); if (dp->buf == NULL) { device_printf(sc->sc_dev, "could not allocate buffer\n"); error = ENOMEM; goto fail; } dp->ni = NULL; } return (0); fail: rsu_free_list(sc, data, ndata); return (error); } static int rsu_alloc_rx_list(struct rsu_softc *sc) { int error, i; error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT, RSU_RXBUFSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < RSU_RX_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); return (0); } static int rsu_alloc_tx_list(struct rsu_softc *sc) { int error, i; error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT, RSU_TXBUFSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_tx_inactive); for (i = 0; i != RSU_N_TRANSFER; i++) { STAILQ_INIT(&sc->sc_tx_active[i]); STAILQ_INIT(&sc->sc_tx_pending[i]); } for (i = 0; i < RSU_TX_LIST_COUNT; i++) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); } return (0); } static void rsu_free_tx_list(struct rsu_softc *sc) { int i; /* prevent further allocations from TX list(s) */ STAILQ_INIT(&sc->sc_tx_inactive); for (i = 0; i != RSU_N_TRANSFER; i++) { STAILQ_INIT(&sc->sc_tx_active[i]); STAILQ_INIT(&sc->sc_tx_pending[i]); } rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT); } static void rsu_free_rx_list(struct rsu_softc *sc) { /* prevent further allocations from RX list(s) */ STAILQ_INIT(&sc->sc_rx_inactive); STAILQ_INIT(&sc->sc_rx_active); rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT); } static void rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata) { int i; for (i = 0; i < ndata; i++) { struct rsu_data *dp = &data[i]; if (dp->buf != NULL) { free(dp->buf, M_USBDEV); dp->buf = NULL; } if (dp->ni != NULL) { ieee80211_free_node(dp->ni); dp->ni = NULL; } } } static struct rsu_data * _rsu_getbuf(struct rsu_softc *sc) { struct rsu_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); else bf = NULL; return (bf); } static struct rsu_data * rsu_getbuf(struct rsu_softc *sc) { struct rsu_data *bf; RSU_ASSERT_LOCKED(sc); bf = _rsu_getbuf(sc); if (bf == NULL) { RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: no buffers\n", __func__); } return (bf); } static void rsu_freebuf(struct rsu_softc *sc, struct rsu_data *bf) { RSU_ASSERT_LOCKED(sc); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, bf, next); } static int rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = R92S_REQ_REGS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); return (rsu_do_request(sc, &req, buf)); } static void rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val) { rsu_write_region_1(sc, addr, &val, 1); } static void rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val) { val = htole16(val); rsu_write_region_1(sc, addr, (uint8_t *)&val, 2); } static void rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val) { val = htole32(val); rsu_write_region_1(sc, addr, (uint8_t *)&val, 4); } static int rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = R92S_REQ_REGS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); return (rsu_do_request(sc, &req, buf)); } static uint8_t rsu_read_1(struct rsu_softc *sc, uint16_t addr) { uint8_t val; if (rsu_read_region_1(sc, addr, &val, 1) != 0) return (0xff); return (val); } static uint16_t rsu_read_2(struct rsu_softc *sc, uint16_t addr) { uint16_t val; if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0) return (0xffff); return (le16toh(val)); } static uint32_t rsu_read_4(struct rsu_softc *sc, uint16_t addr) { uint32_t val; if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0) return (0xffffffff); return (le32toh(val)); } static int rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd) { int ntries; rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd); rsu_ms_delay(sc, 1); for (ntries = 0; ntries < 50; ntries++) { if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0) return (0); rsu_ms_delay(sc, 1); } return (ETIMEDOUT); } static uint8_t rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr) { uint32_t reg; int ntries; reg = rsu_read_4(sc, R92S_EFUSE_CTRL); reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr); reg &= ~R92S_EFUSE_CTRL_VALID; rsu_write_4(sc, R92S_EFUSE_CTRL, reg); /* Wait for read operation to complete. */ for (ntries = 0; ntries < 100; ntries++) { reg = rsu_read_4(sc, R92S_EFUSE_CTRL); if (reg & R92S_EFUSE_CTRL_VALID) return (MS(reg, R92S_EFUSE_CTRL_DATA)); rsu_ms_delay(sc, 1); } device_printf(sc->sc_dev, "could not read efuse byte at address 0x%x\n", addr); return (0xff); } static int rsu_read_rom(struct rsu_softc *sc) { uint8_t *rom = sc->rom; uint16_t addr = 0; uint32_t reg; uint8_t off, msk; int i; /* Make sure that ROM type is eFuse and that autoload succeeded. */ reg = rsu_read_1(sc, R92S_EE_9346CR); if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN) return (EIO); /* Turn on 2.5V to prevent eFuse leakage. */ reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3); rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80); /* Read full ROM image. */ memset(&sc->rom, 0xff, sizeof(sc->rom)); while (addr < 512) { reg = rsu_efuse_read_1(sc, addr); if (reg == 0xff) break; addr++; off = reg >> 4; msk = reg & 0xf; for (i = 0; i < 4; i++) { if (msk & (1 << i)) continue; rom[off * 8 + i * 2 + 0] = rsu_efuse_read_1(sc, addr); addr++; rom[off * 8 + i * 2 + 1] = rsu_efuse_read_1(sc, addr); addr++; } } #ifdef USB_DEBUG if (rsu_debug & RSU_DEBUG_RESET) { /* Dump ROM content. */ printf("\n"); for (i = 0; i < sizeof(sc->rom); i++) printf("%02x:", rom[i]); printf("\n"); } #endif return (0); } static int rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len) { const uint8_t which = RSU_H2C_ENDPOINT; struct rsu_data *data; struct r92s_tx_desc *txd; struct r92s_fw_cmd_hdr *cmd; int cmdsz; int xferlen; RSU_ASSERT_LOCKED(sc); data = rsu_getbuf(sc); if (data == NULL) return (ENOMEM); /* Blank the entire payload, just to be safe */ memset(data->buf, '\0', RSU_TXBUFSZ); /* Round-up command length to a multiple of 8 bytes. */ /* XXX TODO: is this required? */ cmdsz = (len + 7) & ~7; xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz; KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__)); memset(data->buf, 0, xferlen); /* Setup Tx descriptor. */ txd = (struct r92s_tx_desc *)data->buf; txd->txdw0 = htole32( SM(R92S_TXDW0_OFFSET, sizeof(*txd)) | SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) | R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG); txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C)); /* Setup command header. */ cmd = (struct r92s_fw_cmd_hdr *)&txd[1]; cmd->len = htole16(cmdsz); cmd->code = code; cmd->seq = sc->cmd_seq; sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f; /* Copy command payload. */ memcpy(&cmd[1], buf, len); RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FWCMD, "%s: Tx cmd code=0x%x len=0x%x\n", __func__, code, cmdsz); data->buflen = xferlen; STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); usbd_transfer_start(sc->sc_xfer[which]); return (0); } /* ARGSUSED */ static void rsu_calib_task(void *arg, int pending __unused) { struct rsu_softc *sc = arg; #ifdef notyet uint32_t reg; #endif RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: running calibration task\n", __func__); RSU_LOCK(sc); #ifdef notyet /* Read WPS PBC status. */ rsu_write_1(sc, R92S_MAC_PINMUX_CTRL, R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG)); rsu_write_1(sc, R92S_GPIO_IO_SEL, rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS); reg = rsu_read_1(sc, R92S_GPIO_CTRL); if (reg != 0xff && (reg & R92S_GPIO_WPS)) RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "WPS PBC is pushed\n"); #endif /* Read current signal level. */ if (rsu_fw_iocmd(sc, 0xf4000001) == 0) { sc->sc_currssi = rsu_read_4(sc, R92S_IOCMD_DATA); RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: RSSI=%d (%d)\n", __func__, sc->sc_currssi, rsu_hwrssi_to_rssi(sc, sc->sc_currssi)); } if (sc->sc_calibrating) taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz); RSU_UNLOCK(sc); } static void rsu_tx_task(void *arg, int pending __unused) { struct rsu_softc *sc = arg; RSU_LOCK(sc); _rsu_start(sc); RSU_UNLOCK(sc); } #define RSU_PWR_UNKNOWN 0x0 #define RSU_PWR_ACTIVE 0x1 #define RSU_PWR_OFF 0x2 #define RSU_PWR_SLEEP 0x3 /* * Set the current power state. * * The rtlwifi code doesn't do this so aggressively; it * waits for an idle period after association with * no traffic before doing this. * * For now - it's on in all states except RUN, and * in RUN it'll transition to allow sleep. */ struct r92s_pwr_cmd { uint8_t mode; uint8_t smart_ps; uint8_t bcn_pass_time; }; static int rsu_set_fw_power_state(struct rsu_softc *sc, int state) { struct r92s_set_pwr_mode cmd; //struct r92s_pwr_cmd cmd; int error; RSU_ASSERT_LOCKED(sc); /* only change state if required */ if (sc->sc_curpwrstate == state) return (0); memset(&cmd, 0, sizeof(cmd)); switch (state) { case RSU_PWR_ACTIVE: /* Force the hardware awake */ rsu_write_1(sc, R92S_USB_HRPWM, R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON); cmd.mode = R92S_PS_MODE_ACTIVE; break; case RSU_PWR_SLEEP: cmd.mode = R92S_PS_MODE_DTIM; /* XXX configurable? */ cmd.smart_ps = 1; /* XXX 2 if doing p2p */ cmd.bcn_pass_time = 5; /* in 100mS usb.c, linux/rtlwifi */ break; case RSU_PWR_OFF: cmd.mode = R92S_PS_MODE_RADIOOFF; break; default: device_printf(sc->sc_dev, "%s: unknown ps mode (%d)\n", __func__, state); return (ENXIO); } RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting ps mode to %d (mode %d)\n", __func__, state, cmd.mode); error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd)); if (error == 0) sc->sc_curpwrstate = state; return (error); } static void rsu_set_led(struct rsu_softc *sc, int on) { rsu_write_1(sc, R92S_LEDCFG, (rsu_read_1(sc, R92S_LEDCFG) & 0xf0) | (!on << 3)); } static int rsu_monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct rsu_softc *sc = ic->ic_softc; struct rsu_vap *uvp = RSU_VAP(vap); if (vap->iv_state != nstate) { IEEE80211_UNLOCK(ic); RSU_LOCK(sc); switch (nstate) { case IEEE80211_S_INIT: sc->sc_vap_is_running = 0; rsu_set_led(sc, 0); break; case IEEE80211_S_RUN: sc->sc_vap_is_running = 1; rsu_set_led(sc, 1); break; default: /* NOTREACHED */ break; } rsu_rxfilter_refresh(sc); RSU_UNLOCK(sc); IEEE80211_LOCK(ic); } return (uvp->newstate(vap, nstate, arg)); } static int rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct rsu_vap *uvp = RSU_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct rsu_softc *sc = ic->ic_softc; struct ieee80211_node *ni; struct ieee80211_rateset *rs; enum ieee80211_state ostate; int error, startcal = 0; ostate = vap->iv_state; RSU_DPRINTF(sc, RSU_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); if (ostate == IEEE80211_S_RUN) { RSU_LOCK(sc); /* Stop calibration. */ sc->sc_calibrating = 0; /* Pause Tx for AC queues. */ rsu_write_1(sc, R92S_TXPAUSE, R92S_TXPAUSE_AC); usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10)); RSU_UNLOCK(sc); taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task); taskqueue_drain(taskqueue_thread, &sc->tx_task); RSU_LOCK(sc); /* Disassociate from our current BSS. */ rsu_disconnect(sc); usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10)); /* Refresh Rx filter (may be modified by firmware). */ sc->sc_vap_is_running = 0; rsu_rxfilter_refresh(sc); /* Reinstall static keys. */ if (sc->sc_running) rsu_reinit_static_keys(sc); } else RSU_LOCK(sc); switch (nstate) { case IEEE80211_S_INIT: (void) rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); break; case IEEE80211_S_AUTH: ni = ieee80211_ref_node(vap->iv_bss); (void) rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); error = rsu_join_bss(sc, ni); ieee80211_free_node(ni); if (error != 0) { device_printf(sc->sc_dev, "could not send join command\n"); } break; case IEEE80211_S_RUN: /* Flush all AC queues. */ rsu_write_1(sc, R92S_TXPAUSE, 0); ni = ieee80211_ref_node(vap->iv_bss); rs = &ni->ni_rates; /* Indicate highest supported rate. */ ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; (void) rsu_set_fw_power_state(sc, RSU_PWR_SLEEP); ieee80211_free_node(ni); startcal = 1; break; default: break; } if (startcal != 0) { sc->sc_calibrating = 1; /* Start periodic calibration. */ taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz); } RSU_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } static int rsu_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { struct rsu_softc *sc = vap->iv_ic->ic_softc; int is_checked = 0; if (&vap->iv_nw_keys[0] <= k && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { *keyix = ieee80211_crypto_get_key_wepidx(vap, k); } else { if (vap->iv_opmode != IEEE80211_M_STA) { *keyix = 0; /* TODO: obtain keyix from node id */ is_checked = 1; k->wk_flags |= IEEE80211_KEY_SWCRYPT; } else *keyix = R92S_MACID_BSS; } if (!is_checked) { RSU_LOCK(sc); if (isset(sc->keys_bmap, *keyix)) { device_printf(sc->sc_dev, "%s: key slot %d is already used!\n", __func__, *keyix); RSU_UNLOCK(sc); return (0); } setbit(sc->keys_bmap, *keyix); RSU_UNLOCK(sc); } *rxkeyix = *keyix; return (1); } static int rsu_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k, int set) { struct rsu_softc *sc = vap->iv_ic->ic_softc; int ret; if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { /* Not for us. */ return (1); } /* Handle group keys. */ if (&vap->iv_nw_keys[0] <= k && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { KASSERT(k->wk_keyix < nitems(sc->group_keys), ("keyix %u > %zu\n", k->wk_keyix, nitems(sc->group_keys))); RSU_LOCK(sc); sc->group_keys[k->wk_keyix] = (set ? k : NULL); if (!sc->sc_running) { /* Static keys will be set during device startup. */ RSU_UNLOCK(sc); return (1); } if (set) ret = rsu_set_key_group(sc, k); else ret = rsu_delete_key(sc, k->wk_keyix); RSU_UNLOCK(sc); return (!ret); } if (set) { /* wait for pending key removal */ taskqueue_drain(taskqueue_thread, &sc->del_key_task); RSU_LOCK(sc); ret = rsu_set_key_pair(sc, k); RSU_UNLOCK(sc); } else { RSU_DELKEY_BMAP_LOCK(sc); setbit(sc->free_keys_bmap, k->wk_keyix); RSU_DELKEY_BMAP_UNLOCK(sc); /* workaround ieee80211_node_delucastkey() locking */ taskqueue_enqueue(taskqueue_thread, &sc->del_key_task); ret = 0; /* fake success */ } return (!ret); } static int rsu_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) { return (rsu_process_key(vap, k, 1)); } static int rsu_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) { return (rsu_process_key(vap, k, 0)); } static int rsu_cam_read(struct rsu_softc *sc, uint8_t addr, uint32_t *val) { int ntries; rsu_write_4(sc, R92S_CAMCMD, R92S_CAMCMD_POLLING | SM(R92S_CAMCMD_ADDR, addr)); for (ntries = 0; ntries < 10; ntries++) { if (!(rsu_read_4(sc, R92S_CAMCMD) & R92S_CAMCMD_POLLING)) break; usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(1)); } if (ntries == 10) { device_printf(sc->sc_dev, "%s: cannot read CAM entry at address %02X\n", __func__, addr); return (ETIMEDOUT); } *val = rsu_read_4(sc, R92S_CAMREAD); return (0); } static void rsu_cam_write(struct rsu_softc *sc, uint8_t addr, uint32_t data) { rsu_write_4(sc, R92S_CAMWRITE, data); rsu_write_4(sc, R92S_CAMCMD, R92S_CAMCMD_POLLING | R92S_CAMCMD_WRITE | SM(R92S_CAMCMD_ADDR, addr)); } static int rsu_key_check(struct rsu_softc *sc, ieee80211_keyix keyix, int is_valid) { uint32_t val; int error, ntries; for (ntries = 0; ntries < 20; ntries++) { usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(1)); error = rsu_cam_read(sc, R92S_CAM_CTL0(keyix), &val); if (error != 0) { device_printf(sc->sc_dev, "%s: cannot check key status!\n", __func__); return (error); } if (((val & R92S_CAM_VALID) == 0) ^ is_valid) break; } if (ntries == 20) { device_printf(sc->sc_dev, "%s: key %d is %s marked as valid, rejecting request\n", __func__, keyix, is_valid ? "not" : "still"); return (EIO); } return (0); } /* * Map net80211 cipher to RTL8712 security mode. */ static uint8_t rsu_crypto_mode(struct rsu_softc *sc, u_int cipher, int keylen) { switch (cipher) { case IEEE80211_CIPHER_WEP: return keylen < 8 ? R92S_KEY_ALGO_WEP40 : R92S_KEY_ALGO_WEP104; case IEEE80211_CIPHER_TKIP: return R92S_KEY_ALGO_TKIP; case IEEE80211_CIPHER_AES_CCM: return R92S_KEY_ALGO_AES; default: device_printf(sc->sc_dev, "unknown cipher %d\n", cipher); return R92S_KEY_ALGO_INVALID; } } static int rsu_set_key_group(struct rsu_softc *sc, const struct ieee80211_key *k) { struct r92s_fw_cmd_set_key key; uint8_t algo; int error; RSU_ASSERT_LOCKED(sc); /* Map net80211 cipher to HW crypto algorithm. */ algo = rsu_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen); if (algo == R92S_KEY_ALGO_INVALID) return (EINVAL); memset(&key, 0, sizeof(key)); key.algo = algo; key.cam_id = k->wk_keyix; key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0; memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key))); RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD, "%s: keyix %u, group %u, algo %u/%u, flags %04X, len %u, " "macaddr %s\n", __func__, key.cam_id, key.grpkey, k->wk_cipher->ic_cipher, key.algo, k->wk_flags, k->wk_keylen, ether_sprintf(k->wk_macaddr)); error = rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key)); if (error != 0) { device_printf(sc->sc_dev, "%s: cannot send firmware command, error %d\n", __func__, error); return (error); } return (rsu_key_check(sc, k->wk_keyix, 1)); } static int rsu_set_key_pair(struct rsu_softc *sc, const struct ieee80211_key *k) { struct r92s_fw_cmd_set_key_mac key; uint8_t algo; int error; RSU_ASSERT_LOCKED(sc); if (!sc->sc_running) return (ESHUTDOWN); /* Map net80211 cipher to HW crypto algorithm. */ algo = rsu_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen); if (algo == R92S_KEY_ALGO_INVALID) return (EINVAL); memset(&key, 0, sizeof(key)); key.algo = algo; memcpy(key.macaddr, k->wk_macaddr, sizeof(key.macaddr)); memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key))); RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD, "%s: keyix %u, algo %u/%u, flags %04X, len %u, macaddr %s\n", __func__, k->wk_keyix, k->wk_cipher->ic_cipher, key.algo, k->wk_flags, k->wk_keylen, ether_sprintf(key.macaddr)); error = rsu_fw_cmd(sc, R92S_CMD_SET_STA_KEY, &key, sizeof(key)); if (error != 0) { device_printf(sc->sc_dev, "%s: cannot send firmware command, error %d\n", __func__, error); return (error); } return (rsu_key_check(sc, k->wk_keyix, 1)); } static int rsu_reinit_static_keys(struct rsu_softc *sc) { int i, error; for (i = 0; i < nitems(sc->group_keys); i++) { if (sc->group_keys[i] != NULL) { error = rsu_set_key_group(sc, sc->group_keys[i]); if (error != 0) { device_printf(sc->sc_dev, "%s: failed to set static key %d, " "error %d\n", __func__, i, error); return (error); } } } return (0); } static int rsu_delete_key(struct rsu_softc *sc, ieee80211_keyix keyix) { struct r92s_fw_cmd_set_key key; uint32_t val; int error; RSU_ASSERT_LOCKED(sc); if (!sc->sc_running) return (0); /* check if it was automatically removed by firmware */ error = rsu_cam_read(sc, R92S_CAM_CTL0(keyix), &val); if (error == 0 && (val & R92S_CAM_VALID) == 0) { RSU_DPRINTF(sc, RSU_DEBUG_KEY, "%s: key %u does not exist\n", __func__, keyix); clrbit(sc->keys_bmap, keyix); return (0); } memset(&key, 0, sizeof(key)); key.cam_id = keyix; RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD, "%s: removing key %u\n", __func__, key.cam_id); error = rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key)); if (error != 0) { device_printf(sc->sc_dev, "%s: cannot send firmware command, error %d\n", __func__, error); goto finish; } usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(5)); /* * Clear 'valid' bit manually (cannot be done via firmware command). * Used for key check + when firmware command cannot be sent. */ finish: rsu_cam_write(sc, R92S_CAM_CTL0(keyix), 0); clrbit(sc->keys_bmap, keyix); return (rsu_key_check(sc, keyix, 0)); } static void rsu_delete_key_pair_cb(void *arg, int pending __unused) { struct rsu_softc *sc = arg; int i; RSU_DELKEY_BMAP_LOCK(sc); for (i = IEEE80211_WEP_NKID; i < R92S_CAM_ENTRY_LIMIT; i++) { if (isset(sc->free_keys_bmap, i)) { RSU_DELKEY_BMAP_UNLOCK(sc); RSU_LOCK(sc); RSU_DPRINTF(sc, RSU_DEBUG_KEY, "%s: calling rsu_delete_key() with keyix = %d\n", __func__, i); (void) rsu_delete_key(sc, i); RSU_UNLOCK(sc); RSU_DELKEY_BMAP_LOCK(sc); clrbit(sc->free_keys_bmap, i); /* bmap can be changed */ i = IEEE80211_WEP_NKID - 1; continue; } } RSU_DELKEY_BMAP_UNLOCK(sc); } static int rsu_site_survey(struct rsu_softc *sc, struct ieee80211_scan_ssid *ssid) { struct r92s_fw_cmd_sitesurvey cmd; RSU_ASSERT_LOCKED(sc); memset(&cmd, 0, sizeof(cmd)); /* TODO: passive channels? */ if (sc->sc_active_scan) cmd.active = htole32(1); cmd.limit = htole32(48); if (ssid != NULL) { sc->sc_extra_scan = 1; cmd.ssidlen = htole32(ssid->len); memcpy(cmd.ssid, ssid->ssid, ssid->len); } #ifdef USB_DEBUG if (rsu_debug & (RSU_DEBUG_SCAN | RSU_DEBUG_FWCMD)) { device_printf(sc->sc_dev, "sending site survey command, active %d", le32toh(cmd.active)); if (ssid != NULL) { printf(", ssid: "); ieee80211_print_essid(cmd.ssid, le32toh(cmd.ssidlen)); } printf("\n"); } #endif return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd))); } static int rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ndis_wlan_bssid_ex *bss; struct ndis_802_11_fixed_ies *fixed; struct r92s_fw_cmd_auth auth; uint8_t buf[sizeof(*bss) + 128] __aligned(4); uint8_t *frm; uint8_t opmode; int error; RSU_ASSERT_LOCKED(sc); /* Let the FW decide the opmode based on the capinfo field. */ opmode = NDIS802_11AUTOUNKNOWN; RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting operating mode to %d\n", __func__, opmode); error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode)); if (error != 0) return (error); memset(&auth, 0, sizeof(auth)); if (vap->iv_flags & IEEE80211_F_WPA) { auth.mode = R92S_AUTHMODE_WPA; auth.dot1x = (ni->ni_authmode == IEEE80211_AUTH_8021X); } else auth.mode = R92S_AUTHMODE_OPEN; RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting auth mode to %d\n", __func__, auth.mode); error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth)); if (error != 0) return (error); memset(buf, 0, sizeof(buf)); bss = (struct ndis_wlan_bssid_ex *)buf; IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid); bss->ssid.ssidlen = htole32(ni->ni_esslen); memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen); if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA)) bss->privacy = htole32(1); bss->rssi = htole32(ni->ni_avgrssi); if (ic->ic_curmode == IEEE80211_MODE_11B) bss->networktype = htole32(NDIS802_11DS); else bss->networktype = htole32(NDIS802_11OFDM24); bss->config.len = htole32(sizeof(bss->config)); bss->config.bintval = htole32(ni->ni_intval); bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan)); bss->inframode = htole32(NDIS802_11INFRASTRUCTURE); /* XXX verify how this is supposed to look! */ memcpy(bss->supprates, ni->ni_rates.rs_rates, ni->ni_rates.rs_nrates); /* Write the fixed fields of the beacon frame. */ fixed = (struct ndis_802_11_fixed_ies *)&bss[1]; memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8); fixed->bintval = htole16(ni->ni_intval); fixed->capabilities = htole16(ni->ni_capinfo); /* Write IEs to be included in the association request. */ frm = (uint8_t *)&fixed[1]; frm = ieee80211_add_rsn(frm, vap); frm = ieee80211_add_wpa(frm, vap); frm = ieee80211_add_qos(frm, ni); if ((ic->ic_flags & IEEE80211_F_WME) && (ni->ni_ies.wme_ie != NULL)) frm = ieee80211_add_wme_info(frm, &ic->ic_wme); if (ni->ni_flags & IEEE80211_NODE_HT) { frm = ieee80211_add_htcap(frm, ni); frm = ieee80211_add_htinfo(frm, ni); } bss->ieslen = htole32(frm - (uint8_t *)fixed); bss->len = htole32(((frm - buf) + 3) & ~3); RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_FWCMD, "%s: sending join bss command to %s chan %d\n", __func__, ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig)); return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf))); } static int rsu_disconnect(struct rsu_softc *sc) { uint32_t zero = 0; /* :-) */ /* Disassociate from our current BSS. */ RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, "%s: sending disconnect command\n", __func__); return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero))); } /* * Map the hardware provided RSSI value to a signal level. * For the most part it's just something we divide by and cap * so it doesn't overflow the representation by net80211. */ static int rsu_hwrssi_to_rssi(struct rsu_softc *sc, int hw_rssi) { int v; if (hw_rssi == 0) return (0); v = hw_rssi >> 4; if (v > 80) v = 80; return (v); } CTASSERT(MCLBYTES > sizeof(struct ieee80211_frame)); static void rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ndis_wlan_bssid_ex *bss; struct ieee80211_rx_stats rxs; struct mbuf *m; uint32_t ieslen; uint32_t pktlen; if (__predict_false(len < sizeof(*bss))) return; bss = (struct ndis_wlan_bssid_ex *)buf; ieslen = le32toh(bss->ieslen); /* range check length of information element */ if (__predict_false(ieslen > (uint32_t)(len - sizeof(*bss)))) return; RSU_DPRINTF(sc, RSU_DEBUG_SCAN, "%s: found BSS %s: len=%d chan=%d inframode=%d " "networktype=%d privacy=%d, RSSI=%d\n", __func__, ether_sprintf(bss->macaddr), ieslen, le32toh(bss->config.dsconfig), le32toh(bss->inframode), le32toh(bss->networktype), le32toh(bss->privacy), le32toh(bss->rssi)); /* Build a fake beacon frame to let net80211 do all the parsing. */ /* XXX TODO: just call the new scan API methods! */ if (__predict_false(ieslen > (size_t)(MCLBYTES - sizeof(*wh)))) return; pktlen = sizeof(*wh) + ieslen; m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) return; wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; USETW(wh->i_dur, 0); IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr); IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr); *(uint16_t *)wh->i_seq = 0; memcpy(&wh[1], (uint8_t *)&bss[1], ieslen); /* Finalize mbuf. */ m->m_pkthdr.len = m->m_len = pktlen; /* Set channel flags for input path */ bzero(&rxs, sizeof(rxs)); rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; rxs.c_ieee = le32toh(bss->config.dsconfig); rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ); /* This is a number from 0..100; so let's just divide it down a bit */ rxs.c_rssi = le32toh(bss->rssi) / 2; rxs.c_nf = -96; if (ieee80211_add_rx_params(m, &rxs) == 0) return; /* XXX avoid a LOR */ RSU_UNLOCK(sc); ieee80211_input_mimo_all(ic, m); RSU_LOCK(sc); } static void rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni = vap->iv_bss; struct r92s_event_join_bss *rsp; uint32_t tmp; int res; if (__predict_false(len < sizeof(*rsp))) return; rsp = (struct r92s_event_join_bss *)buf; res = (int)le32toh(rsp->join_res); RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, "%s: Rx join BSS event len=%d res=%d\n", __func__, len, res); /* * XXX Don't do this; there's likely a better way to tell * the caller we failed. */ if (res <= 0) { RSU_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); RSU_LOCK(sc); return; } tmp = le32toh(rsp->associd); if (tmp >= vap->iv_max_aid) { RSU_DPRINTF(sc, RSU_DEBUG_ANY, "Assoc ID overflow\n"); tmp = 1; } RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, "%s: associated with %s associd=%d\n", __func__, ether_sprintf(rsp->bss.macaddr), tmp); /* XXX is this required? What's the top two bits for again? */ ni->ni_associd = tmp | 0xc000; /* Refresh Rx filter (was changed by firmware). */ sc->sc_vap_is_running = 1; rsu_rxfilter_refresh(sc); RSU_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_RUN, IEEE80211_FC0_SUBTYPE_ASSOC_RESP); RSU_LOCK(sc); } static void rsu_event_addba_req_report(struct rsu_softc *sc, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct r92s_add_ba_event *ba = (void *) buf; struct ieee80211_node *ni; if (len < sizeof(*ba)) { device_printf(sc->sc_dev, "%s: short read (%d)\n", __func__, len); return; } if (vap == NULL) return; RSU_DPRINTF(sc, RSU_DEBUG_AMPDU, "%s: mac=%s, tid=%d, ssn=%d\n", __func__, ether_sprintf(ba->mac_addr), (int) ba->tid, (int) le16toh(ba->ssn)); /* XXX do node lookup; this is STA specific */ ni = ieee80211_ref_node(vap->iv_bss); ieee80211_ampdu_rx_start_ext(ni, ba->tid, le16toh(ba->ssn) >> 4, 32); ieee80211_free_node(ni); } static void rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD, "%s: Rx event code=%d len=%d\n", __func__, code, len); switch (code) { case R92S_EVT_SURVEY: rsu_event_survey(sc, buf, len); break; case R92S_EVT_SURVEY_DONE: RSU_DPRINTF(sc, RSU_DEBUG_SCAN, "%s: %s scan done, found %d BSS\n", __func__, sc->sc_extra_scan ? "direct" : "broadcast", le32toh(*(uint32_t *)buf)); if (sc->sc_extra_scan == 1) { /* Send broadcast probe request. */ sc->sc_extra_scan = 0; if (vap != NULL && rsu_site_survey(sc, NULL) != 0) { RSU_UNLOCK(sc); ieee80211_cancel_scan(vap); RSU_LOCK(sc); } break; } if (vap != NULL) { RSU_UNLOCK(sc); ieee80211_scan_done(vap); RSU_LOCK(sc); } break; case R92S_EVT_JOIN_BSS: if (vap->iv_state == IEEE80211_S_AUTH) rsu_event_join_bss(sc, buf, len); break; case R92S_EVT_DEL_STA: RSU_DPRINTF(sc, RSU_DEBUG_FWCMD | RSU_DEBUG_STATE, "%s: disassociated from %s\n", __func__, ether_sprintf(buf)); if (vap->iv_state == IEEE80211_S_RUN && IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) { RSU_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); RSU_LOCK(sc); } break; case R92S_EVT_WPS_PBC: RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD, "%s: WPS PBC pushed.\n", __func__); break; case R92S_EVT_FWDBG: buf[60] = '\0'; RSU_DPRINTF(sc, RSU_DEBUG_FWDBG, "FWDBG: %s\n", (char *)buf); break; case R92S_EVT_ADDBA_REQ_REPORT: rsu_event_addba_req_report(sc, buf, len); break; default: device_printf(sc->sc_dev, "%s: unhandled code (%d)\n", __func__, code); break; } } static void rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len) { struct r92s_fw_cmd_hdr *cmd; int cmdsz; RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx events len=%d\n", __func__, len); /* Skip Rx status. */ buf += sizeof(struct r92s_rx_stat); len -= sizeof(struct r92s_rx_stat); /* Process all events. */ for (;;) { /* Check that command header fits. */ if (__predict_false(len < sizeof(*cmd))) break; cmd = (struct r92s_fw_cmd_hdr *)buf; /* Check that command payload fits. */ cmdsz = le16toh(cmd->len); if (__predict_false(len < sizeof(*cmd) + cmdsz)) break; /* Process firmware event. */ rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz); if (!(cmd->seq & R92S_FW_CMD_MORE)) break; buf += sizeof(*cmd) + cmdsz; len -= sizeof(*cmd) + cmdsz; } } static int8_t rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt) { static const int8_t cckoff[] = { 14, -2, -20, -40 }; struct r92s_rx_phystat *phy; struct r92s_rx_cck *cck; uint8_t rpt; int8_t rssi; if (rate <= 3) { cck = (struct r92s_rx_cck *)physt; rpt = (cck->agc_rpt >> 6) & 0x3; rssi = cck->agc_rpt & 0x3e; rssi = cckoff[rpt] - rssi; } else { /* OFDM/HT. */ phy = (struct r92s_rx_phystat *)physt; rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106; } return (rssi); } static struct mbuf * rsu_rx_copy_to_mbuf(struct rsu_softc *sc, struct r92s_rx_stat *stat, int totlen) { struct ieee80211com *ic = &sc->sc_ic; struct mbuf *m; uint32_t rxdw0; int pktlen; rxdw0 = le32toh(stat->rxdw0); if (__predict_false(rxdw0 & (R92S_RXDW0_CRCERR | R92S_RXDW0_ICVERR))) { RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: RX flags error (%s)\n", __func__, rxdw0 & R92S_RXDW0_CRCERR ? "CRC" : "ICV"); goto fail; } pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN); if (__predict_false(pktlen < sizeof (struct ieee80211_frame_ack))) { RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: frame is too short: %d\n", __func__, pktlen); goto fail; } m = m_get2(totlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) { device_printf(sc->sc_dev, "%s: could not allocate RX mbuf, totlen %d\n", __func__, totlen); goto fail; } /* Finalize mbuf. */ memcpy(mtod(m, uint8_t *), (uint8_t *)stat, totlen); m->m_pkthdr.len = m->m_len = totlen; return (m); fail: counter_u64_add(ic->ic_ierrors, 1); return (NULL); } static uint32_t rsu_get_tsf_low(struct rsu_softc *sc) { return (rsu_read_4(sc, R92S_TSFTR)); } static uint32_t rsu_get_tsf_high(struct rsu_softc *sc) { return (rsu_read_4(sc, R92S_TSFTR + 4)); } static struct ieee80211_node * rsu_rx_frame(struct rsu_softc *sc, struct mbuf *m) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame_min *wh; struct ieee80211_rx_stats rxs; struct r92s_rx_stat *stat; uint32_t rxdw0, rxdw3; uint8_t cipher, rate; int infosz; int rssi; stat = mtod(m, struct r92s_rx_stat *); rxdw0 = le32toh(stat->rxdw0); rxdw3 = le32toh(stat->rxdw3); rate = MS(rxdw3, R92S_RXDW3_RATE); cipher = MS(rxdw0, R92S_RXDW0_CIPHER); infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8; /* Get RSSI from PHY status descriptor if present. */ if (infosz != 0 && (rxdw0 & R92S_RXDW0_PHYST)) rssi = rsu_get_rssi(sc, rate, &stat[1]); else { /* Cheat and get the last calibrated RSSI */ rssi = rsu_hwrssi_to_rssi(sc, sc->sc_currssi); } /* Hardware does Rx TCP checksum offload. */ /* * This flag can be set for some other * (e.g., EAPOL) frame types, so don't rely on it. */ if (rxdw3 & R92S_RXDW3_TCPCHKVALID) { RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: TCP/IP checksums: %schecked / %schecked\n", __func__, (rxdw3 & R92S_RXDW3_TCPCHKRPT) ? "" : "not ", (rxdw3 & R92S_RXDW3_IPCHKRPT) ? "" : "not "); /* * 'IP header checksum valid' bit will not be set if * the frame was not checked / has incorrect checksum / * does not have checksum (IPv6). * * NB: if DF bit is not set then frame will not be checked. */ if (rxdw3 & R92S_RXDW3_IPCHKRPT) { m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; m->m_pkthdr.csum_flags |= CSUM_IP_VALID; } /* * This is independent of the above check. */ if (rxdw3 & R92S_RXDW3_TCPCHKRPT) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; m->m_pkthdr.csum_flags |= CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } /* RX flags */ /* Set channel flags for input path */ bzero(&rxs, sizeof(rxs)); /* normal RSSI */ rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; rxs.c_rssi = rssi; rxs.c_nf = -96; /* Rate */ if (rate < 12) { rxs.c_rate = ridx2rate[rate]; if (RSU_RATE_IS_CCK(rate)) rxs.c_pktflags |= IEEE80211_RX_F_CCK; else rxs.c_pktflags |= IEEE80211_RX_F_OFDM; } else { rxs.c_rate = IEEE80211_RATE_MCS | (rate - 12); rxs.c_pktflags |= IEEE80211_RX_F_HT; } if (ieee80211_radiotap_active(ic)) { struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap; /* Map HW rate index to 802.11 rate. */ tap->wr_flags = 0; /* TODO */ tap->wr_tsft = rsu_get_tsf_high(sc); if (le32toh(stat->tsf_low) > rsu_get_tsf_low(sc)) tap->wr_tsft--; tap->wr_tsft = (uint64_t)htole32(tap->wr_tsft) << 32; tap->wr_tsft += stat->tsf_low; tap->wr_rate = rxs.c_rate; tap->wr_dbm_antsignal = rssi; tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); }; (void) ieee80211_add_rx_params(m, &rxs); /* Drop descriptor. */ m_adj(m, sizeof(*stat) + infosz); wh = mtod(m, struct ieee80211_frame_min *); if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && cipher != R92S_KEY_ALGO_NONE) { m->m_flags |= M_WEP; } RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx frame len %d, rate %d, infosz %d\n", __func__, m->m_len, rate, infosz); if (m->m_len >= sizeof(*wh)) return (ieee80211_find_rxnode(ic, wh)); return (NULL); } static struct mbuf * rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len) { struct r92s_rx_stat *stat; uint32_t rxdw0; int totlen, pktlen, infosz, npkts; struct mbuf *m, *m0 = NULL, *prevm = NULL; /* * don't pass packets to the ieee80211 framework if the driver isn't * RUNNING. */ if (!sc->sc_running) return (NULL); /* Get the number of encapsulated frames. */ stat = (struct r92s_rx_stat *)buf; npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT); RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx %d frames in one chunk\n", __func__, npkts); /* Process all of them. */ while (npkts-- > 0) { if (__predict_false(len < sizeof(*stat))) break; stat = (struct r92s_rx_stat *)buf; rxdw0 = le32toh(stat->rxdw0); pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN); if (__predict_false(pktlen == 0)) break; infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8; /* Make sure everything fits in xfer. */ totlen = sizeof(*stat) + infosz + pktlen; if (__predict_false(totlen > len)) break; /* Process 802.11 frame. */ m = rsu_rx_copy_to_mbuf(sc, stat, totlen); if (m0 == NULL) m0 = m; if (prevm == NULL) prevm = m; else { prevm->m_next = m; prevm = m; } /* Next chunk is 128-byte aligned. */ totlen = (totlen + 127) & ~127; buf += totlen; len -= totlen; } return (m0); } static struct mbuf * rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data) { struct rsu_softc *sc = data->sc; struct ieee80211com *ic = &sc->sc_ic; struct r92s_rx_stat *stat; int len; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); if (__predict_false(len < sizeof(*stat))) { RSU_DPRINTF(sc, RSU_DEBUG_RX, "xfer too short %d\n", len); counter_u64_add(ic->ic_ierrors, 1); return (NULL); } /* Determine if it is a firmware C2H event or an 802.11 frame. */ stat = (struct r92s_rx_stat *)data->buf; if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) { rsu_rx_multi_event(sc, data->buf, len); /* No packets to process. */ return (NULL); } else return (rsu_rx_multi_frame(sc, data->buf, len)); } static void rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct rsu_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct mbuf *m = NULL, *next; struct rsu_data *data; RSU_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_rx_active); if (data == NULL) goto tr_setup; STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); m = rsu_rxeof(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) { KASSERT(m == NULL, ("mbuf isn't NULL")); return; } STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ while (m != NULL) { next = m->m_next; m->m_next = NULL; ni = rsu_rx_frame(sc, m); RSU_UNLOCK(sc); if (ni != NULL) { if (ni->ni_flags & IEEE80211_NODE_HT) m->m_flags |= M_AMPDU; (void)ieee80211_input_mimo(ni, m); ieee80211_free_node(ni); } else (void)ieee80211_input_mimo_all(ic, m); RSU_LOCK(sc); m = next; } break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&sc->sc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } break; } } static void rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data) { #ifdef USB_DEBUG struct rsu_softc *sc = usbd_xfer_softc(xfer); #endif RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: called; data=%p\n", __func__, data); if (data->m) { /* XXX status? */ ieee80211_tx_complete(data->ni, data->m, 0); data->m = NULL; data->ni = NULL; } } static void rsu_bulk_tx_callback_sub(struct usb_xfer *xfer, usb_error_t error, uint8_t which) { struct rsu_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct rsu_data *data; RSU_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active[which]); if (data == NULL) goto tr_setup; RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: transfer done %p\n", __func__, data); STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); rsu_txeof(xfer, data); rsu_freebuf(sc, data); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->sc_tx_pending[which]); if (data == NULL) { RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: empty pending queue sc %p\n", __func__, sc); return; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next); STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: submitting transfer %p\n", __func__, data); usbd_transfer_submit(xfer); break; default: data = STAILQ_FIRST(&sc->sc_tx_active[which]); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); rsu_txeof(xfer, data); rsu_freebuf(sc, data); } counter_u64_add(ic->ic_oerrors, 1); if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto tr_setup; } break; } /* * XXX TODO: if the queue is low, flush out FF TX frames. * Remember to unlock the driver for now; net80211 doesn't * defer it for us. */ } static void rsu_bulk_tx_callback_be_bk(struct usb_xfer *xfer, usb_error_t error) { struct rsu_softc *sc = usbd_xfer_softc(xfer); rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_BE_BK); /* This kicks the TX taskqueue */ rsu_start(sc); } static void rsu_bulk_tx_callback_vi_vo(struct usb_xfer *xfer, usb_error_t error) { struct rsu_softc *sc = usbd_xfer_softc(xfer); rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_VI_VO); /* This kicks the TX taskqueue */ rsu_start(sc); } static void rsu_bulk_tx_callback_h2c(struct usb_xfer *xfer, usb_error_t error) { struct rsu_softc *sc = usbd_xfer_softc(xfer); rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_H2C); /* This kicks the TX taskqueue */ rsu_start(sc); } /* * Transmit the given frame. * * This doesn't free the node or mbuf upon failure. */ static int rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, struct rsu_data *data) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; struct r92s_tx_desc *txd; uint8_t type, cipher; int prio = 0; uint8_t which; int hasqos; int xferlen; int qid; RSU_ASSERT_LOCKED(sc); wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: data=%p, m=%p\n", __func__, data, m0); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { device_printf(sc->sc_dev, "ieee80211_crypto_encap returns NULL.\n"); /* XXX we don't expect the fragmented frames */ return (ENOBUFS); } wh = mtod(m0, struct ieee80211_frame *); } /* If we have QoS then use it */ /* XXX TODO: mbuf WME/PRI versus TID? */ if (IEEE80211_QOS_HAS_SEQ(wh)) { /* Has QoS */ prio = M_WME_GETAC(m0); which = rsu_wme_ac_xfer_map[prio]; hasqos = 1; } else { /* Non-QoS TID */ /* XXX TODO: tid=0 for non-qos TID? */ which = rsu_wme_ac_xfer_map[WME_AC_BE]; hasqos = 0; prio = 0; } qid = rsu_ac2qid[prio]; #if 0 switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: which = rsu_wme_ac_xfer_map[WME_AC_VO]; break; default: which = rsu_wme_ac_xfer_map[M_WME_GETAC(m0)]; break; } hasqos = 0; #endif RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: pri=%d, which=%d, hasqos=%d\n", __func__, prio, which, hasqos); /* Fill Tx descriptor. */ txd = (struct r92s_tx_desc *)data->buf; memset(txd, 0, sizeof(*txd)); txd->txdw0 |= htole32( SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) | SM(R92S_TXDW0_OFFSET, sizeof(*txd)) | R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG); txd->txdw1 |= htole32( SM(R92S_TXDW1_MACID, R92S_MACID_BSS) | SM(R92S_TXDW1_QSEL, qid)); if (!hasqos) txd->txdw1 |= htole32(R92S_TXDW1_NONQOS); if (k != NULL && !(k->wk_flags & IEEE80211_KEY_SWENCRYPT)) { switch (k->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_WEP: cipher = R92S_TXDW1_CIPHER_WEP; break; case IEEE80211_CIPHER_TKIP: cipher = R92S_TXDW1_CIPHER_TKIP; break; case IEEE80211_CIPHER_AES_CCM: cipher = R92S_TXDW1_CIPHER_AES; break; default: cipher = R92S_TXDW1_CIPHER_NONE; } txd->txdw1 |= htole32( SM(R92S_TXDW1_CIPHER, cipher) | SM(R92S_TXDW1_KEYIDX, k->wk_keyix)); } /* XXX todo: set AGGEN bit if appropriate? */ txd->txdw2 |= htole32(R92S_TXDW2_BK); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txd->txdw2 |= htole32(R92S_TXDW2_BMCAST); /* * Firmware will use and increment the sequence number for the * specified priority. */ txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, prio)); if (ieee80211_radiotap_active_vap(vap)) { struct rsu_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); ieee80211_radiotap_tx(vap, m0); } xferlen = sizeof(*txd) + m0->m_pkthdr.len; m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]); data->buflen = xferlen; data->ni = ni; data->m = m0; STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); /* start transfer, if any */ usbd_transfer_start(sc->sc_xfer[which]); return (0); } static int rsu_transmit(struct ieee80211com *ic, struct mbuf *m) { struct rsu_softc *sc = ic->ic_softc; int error; RSU_LOCK(sc); if (!sc->sc_running) { RSU_UNLOCK(sc); return (ENXIO); } /* * XXX TODO: ensure that we treat 'm' as a list of frames * to transmit! */ error = mbufq_enqueue(&sc->sc_snd, m); if (error) { RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: mbufq_enable: failed (%d)\n", __func__, error); RSU_UNLOCK(sc); return (error); } RSU_UNLOCK(sc); /* This kicks the TX taskqueue */ rsu_start(sc); return (0); } static void rsu_drain_mbufq(struct rsu_softc *sc) { struct mbuf *m; struct ieee80211_node *ni; RSU_ASSERT_LOCKED(sc); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; ieee80211_free_node(ni); m_freem(m); } } static void _rsu_start(struct rsu_softc *sc) { struct ieee80211_node *ni; struct rsu_data *bf; struct mbuf *m; RSU_ASSERT_LOCKED(sc); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { bf = rsu_getbuf(sc); if (bf == NULL) { RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: failed to get buffer\n", __func__); mbufq_prepend(&sc->sc_snd, m); break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; if (rsu_tx_start(sc, ni, m, bf) != 0) { RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: failed to transmit\n", __func__); if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); rsu_freebuf(sc, bf); ieee80211_free_node(ni); m_freem(m); break; } } } static void rsu_start(struct rsu_softc *sc) { taskqueue_enqueue(taskqueue_thread, &sc->tx_task); } static int rsu_ioctl_net(struct ieee80211com *ic, u_long cmd, void *data) { struct rsu_softc *sc = ic->ic_softc; struct ifreq *ifr = (struct ifreq *)data; int error; error = 0; switch (cmd) { case SIOCSIFCAP: { struct ieee80211vap *vap; int rxmask; rxmask = ifr->ifr_reqcap & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6); RSU_LOCK(sc); /* Both RXCSUM bits must be set (or unset). */ if (sc->sc_rx_checksum_enable && rxmask != (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { rxmask = 0; sc->sc_rx_checksum_enable = 0; rsu_rxfilter_set(sc, R92S_RCR_TCP_OFFLD_EN, 0); } else if (!sc->sc_rx_checksum_enable && rxmask != 0) { rxmask = IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; sc->sc_rx_checksum_enable = 1; rsu_rxfilter_set(sc, 0, R92S_RCR_TCP_OFFLD_EN); } else { /* Nothing to do. */ RSU_UNLOCK(sc); break; } RSU_UNLOCK(sc); IEEE80211_LOCK(ic); /* XXX */ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { struct ifnet *ifp = vap->iv_ifp; ifp->if_capenable &= ~(IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6); ifp->if_capenable |= rxmask; } IEEE80211_UNLOCK(ic); break; } default: error = ENOTTY; /* for net80211 */ break; } return (error); } static void rsu_parent(struct ieee80211com *ic) { struct rsu_softc *sc = ic->ic_softc; if (ic->ic_nrunning > 0) { if (rsu_init(sc) == 0) ieee80211_start_all(ic); else { struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap != NULL) ieee80211_stop(vap); } } else rsu_stop(sc); } /* * Power on sequence for A-cut adapters. */ static void rsu_power_on_acut(struct rsu_softc *sc) { uint32_t reg; rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53); rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57); /* Enable AFE macro block's bandgap and Mbias. */ rsu_write_1(sc, R92S_AFE_MISC, rsu_read_1(sc, R92S_AFE_MISC) | R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN); /* Enable LDOA15 block. */ rsu_write_1(sc, R92S_LDOA15_CTRL, rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN); rsu_write_1(sc, R92S_SPS1_CTRL, rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN); rsu_ms_delay(sc, 2000); /* Enable switch regulator block. */ rsu_write_1(sc, R92S_SPS1_CTRL, rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN); rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08); rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90); /* Enable AFE clock. */ rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1, rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04); /* Enable AFE PLL macro block. */ rsu_write_1(sc, R92S_AFE_PLL_CTRL, rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11); /* Attach AFE PLL to MACTOP/BB. */ rsu_write_1(sc, R92S_SYS_ISO_CTRL, rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11); /* Switch to 40MHz clock instead of 80MHz. */ rsu_write_2(sc, R92S_SYS_CLKR, rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL); /* Enable MAC clock. */ rsu_write_2(sc, R92S_SYS_CLKR, rsu_read_2(sc, R92S_SYS_CLKR) | R92S_MAC_CLK_EN | R92S_SYS_CLK_EN); rsu_write_1(sc, R92S_PMC_FSM, 0x02); /* Enable digital core and IOREG R/W. */ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08); rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80); /* Switch the control path to firmware. */ reg = rsu_read_2(sc, R92S_SYS_CLKR); reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL; rsu_write_2(sc, R92S_SYS_CLKR, reg); rsu_write_2(sc, R92S_CR, 0x37fc); /* Fix USB RX FIFO issue. */ rsu_write_1(sc, 0xfe5c, rsu_read_1(sc, 0xfe5c) | 0x80); rsu_write_1(sc, 0x00ab, rsu_read_1(sc, 0x00ab) | 0xc0); rsu_write_1(sc, R92S_SYS_CLKR, rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL); } /* * Power on sequence for B-cut and C-cut adapters. */ static void rsu_power_on_bcut(struct rsu_softc *sc) { uint32_t reg; int ntries; /* Prevent eFuse leakage. */ rsu_write_1(sc, 0x37, 0xb0); rsu_ms_delay(sc, 10); rsu_write_1(sc, 0x37, 0x30); /* Switch the control path to hardware. */ reg = rsu_read_2(sc, R92S_SYS_CLKR); if (reg & R92S_FWHW_SEL) { rsu_write_2(sc, R92S_SYS_CLKR, reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL)); } rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53); rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57); reg = rsu_read_1(sc, R92S_AFE_MISC); rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN); rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN); /* Enable PLL. */ rsu_write_1(sc, R92S_LDOA15_CTRL, rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN); rsu_write_1(sc, R92S_LDOV12D_CTRL, rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08); rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20); /* Support 64KB IMEM. */ rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97); /* Enable AFE clock. */ rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1, rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04); /* Enable AFE PLL macro block. */ reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL); rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11); rsu_ms_delay(sc, 1); /* Attach AFE PLL to MACTOP/BB. */ rsu_write_1(sc, R92S_SYS_ISO_CTRL, rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11); /* Switch to 40MHz clock. */ rsu_write_1(sc, R92S_SYS_CLKR, 0x00); /* Disable CPU clock and 80MHz SSC. */ rsu_write_1(sc, R92S_SYS_CLKR, rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0); /* Enable MAC clock. */ rsu_write_2(sc, R92S_SYS_CLKR, rsu_read_2(sc, R92S_SYS_CLKR) | R92S_MAC_CLK_EN | R92S_SYS_CLK_EN); rsu_write_1(sc, R92S_PMC_FSM, 0x02); /* Enable digital core and IOREG R/W. */ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08); rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80); /* Switch the control path to firmware. */ reg = rsu_read_2(sc, R92S_SYS_CLKR); reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL; rsu_write_2(sc, R92S_SYS_CLKR, reg); rsu_write_2(sc, R92S_CR, 0x37fc); /* Fix USB RX FIFO issue. */ rsu_write_1(sc, 0xfe5c, rsu_read_1(sc, 0xfe5c) | 0x80); rsu_write_1(sc, R92S_SYS_CLKR, rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL); rsu_write_1(sc, 0xfe1c, 0x80); /* Make sure TxDMA is ready to download firmware. */ for (ntries = 0; ntries < 20; ntries++) { reg = rsu_read_1(sc, R92S_TCR); if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) == (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) break; rsu_ms_delay(sc, 1); } if (ntries == 20) { RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_TX, "%s: TxDMA is not ready\n", __func__); /* Reset TxDMA. */ reg = rsu_read_1(sc, R92S_CR); rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN); } } static void rsu_power_off(struct rsu_softc *sc) { /* Turn RF off. */ rsu_write_1(sc, R92S_RF_CTRL, 0x00); rsu_ms_delay(sc, 5); /* Turn MAC off. */ /* Switch control path. */ rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38); /* Reset MACTOP. */ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70); rsu_write_1(sc, R92S_PMC_FSM, 0x06); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8); /* Disable AFE PLL. */ rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00); /* Disable A15V. */ rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54); /* Disable eFuse 1.2V. */ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50); rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24); /* Enable AFE macro block's bandgap and Mbias. */ rsu_write_1(sc, R92S_AFE_MISC, 0x30); /* Disable 1.6V LDO. */ rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56); rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43); /* Firmware - tell it to switch things off */ (void) rsu_set_fw_power_state(sc, RSU_PWR_OFF); } static int rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len) { const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO]; struct rsu_data *data; struct r92s_tx_desc *txd; int mlen; while (len > 0) { data = rsu_getbuf(sc); if (data == NULL) return (ENOMEM); txd = (struct r92s_tx_desc *)data->buf; memset(txd, 0, sizeof(*txd)); if (len <= RSU_TXBUFSZ - sizeof(*txd)) { /* Last chunk. */ txd->txdw0 |= htole32(R92S_TXDW0_LINIP); mlen = len; } else mlen = RSU_TXBUFSZ - sizeof(*txd); txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen)); memcpy(&txd[1], buf, mlen); data->buflen = sizeof(*txd) + mlen; RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FW | RSU_DEBUG_RESET, "%s: starting transfer %p\n", __func__, data); STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); buf += mlen; len -= mlen; } usbd_transfer_start(sc->sc_xfer[which]); return (0); } CTASSERT(sizeof(size_t) >= sizeof(uint32_t)); static int rsu_load_firmware(struct rsu_softc *sc) { const struct r92s_fw_hdr *hdr; struct r92s_fw_priv *dmem; struct ieee80211com *ic = &sc->sc_ic; const uint8_t *imem, *emem; uint32_t imemsz, ememsz; const struct firmware *fw; size_t size; uint32_t reg; int ntries, error; if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY) { RSU_DPRINTF(sc, RSU_DEBUG_ANY, "%s: Firmware already loaded\n", __func__); return (0); } RSU_UNLOCK(sc); /* Read firmware image from the filesystem. */ if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) { device_printf(sc->sc_dev, "%s: failed load firmware of file rsu-rtl8712fw\n", __func__); RSU_LOCK(sc); return (ENXIO); } RSU_LOCK(sc); size = fw->datasize; if (size < sizeof(*hdr)) { device_printf(sc->sc_dev, "firmware too short\n"); error = EINVAL; goto fail; } hdr = (const struct r92s_fw_hdr *)fw->data; if (hdr->signature != htole16(0x8712) && hdr->signature != htole16(0x8192)) { device_printf(sc->sc_dev, "invalid firmware signature 0x%x\n", le16toh(hdr->signature)); error = EINVAL; goto fail; } RSU_DPRINTF(sc, RSU_DEBUG_FW, "FW V%d %02x-%02x %02x:%02x\n", le16toh(hdr->version), hdr->month, hdr->day, hdr->hour, hdr->minute); /* Make sure that driver and firmware are in sync. */ if (hdr->privsz != htole32(sizeof(*dmem))) { device_printf(sc->sc_dev, "unsupported firmware image\n"); error = EINVAL; goto fail; } /* Get FW sections sizes. */ imemsz = le32toh(hdr->imemsz); ememsz = le32toh(hdr->sramsz); /* Check that all FW sections fit in image. */ if (imemsz > (size_t)(size - sizeof(*hdr)) || ememsz > (size_t)(size - sizeof(*hdr) - imemsz)) { device_printf(sc->sc_dev, "firmware too short\n"); error = EINVAL; goto fail; } imem = (const uint8_t *)&hdr[1]; emem = imem + imemsz; /* Load IMEM section. */ error = rsu_fw_loadsection(sc, imem, imemsz); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware section %s\n", "IMEM"); goto fail; } /* Wait for load to complete. */ for (ntries = 0; ntries != 50; ntries++) { rsu_ms_delay(sc, 10); reg = rsu_read_1(sc, R92S_TCR); if (reg & R92S_TCR_IMEM_CODE_DONE) break; } if (ntries == 50) { device_printf(sc->sc_dev, "timeout waiting for IMEM transfer\n"); error = ETIMEDOUT; goto fail; } /* Load EMEM section. */ error = rsu_fw_loadsection(sc, emem, ememsz); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware section %s\n", "EMEM"); goto fail; } /* Wait for load to complete. */ for (ntries = 0; ntries != 50; ntries++) { rsu_ms_delay(sc, 10); reg = rsu_read_2(sc, R92S_TCR); if (reg & R92S_TCR_EMEM_CODE_DONE) break; } if (ntries == 50) { device_printf(sc->sc_dev, "timeout waiting for EMEM transfer\n"); error = ETIMEDOUT; goto fail; } /* Enable CPU. */ rsu_write_1(sc, R92S_SYS_CLKR, rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL); if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) { device_printf(sc->sc_dev, "could not enable system clock\n"); error = EIO; goto fail; } rsu_write_2(sc, R92S_SYS_FUNC_EN, rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN); if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) { device_printf(sc->sc_dev, "could not enable microcontroller\n"); error = EIO; goto fail; } /* Wait for CPU to initialize. */ for (ntries = 0; ntries < 100; ntries++) { if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_IMEM_RDY) break; rsu_ms_delay(sc, 1); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for microcontroller\n"); error = ETIMEDOUT; goto fail; } /* Update DMEM section before loading. */ dmem = __DECONST(struct r92s_fw_priv *, &hdr->priv); memset(dmem, 0, sizeof(*dmem)); dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172; dmem->nendpoints = sc->sc_nendpoints; dmem->chip_version = sc->cut; dmem->rf_config = sc->sc_rftype; dmem->vcs_type = R92S_VCS_TYPE_AUTO; dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS; dmem->turbo_mode = 0; dmem->bw40_en = !! (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40); dmem->amsdu2ampdu_en = !! (sc->sc_ht); dmem->ampdu_en = !! (sc->sc_ht); dmem->agg_offload = !! (sc->sc_ht); dmem->qos_en = 1; dmem->ps_offload = 1; dmem->lowpower_mode = 1; /* XXX TODO: configurable? */ /* Load DMEM section. */ error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem)); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware section %s\n", "DMEM"); goto fail; } /* Wait for load to complete. */ for (ntries = 0; ntries < 100; ntries++) { if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE) break; rsu_ms_delay(sc, 1); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for %s transfer\n", "DMEM"); error = ETIMEDOUT; goto fail; } /* Wait for firmware readiness. */ for (ntries = 0; ntries < 60; ntries++) { if (!(rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY)) break; rsu_ms_delay(sc, 1); } if (ntries == 60) { device_printf(sc->sc_dev, "timeout waiting for firmware readiness\n"); error = ETIMEDOUT; goto fail; } fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static int rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct rsu_softc *sc = ic->ic_softc; struct rsu_data *bf; /* prevent management frames from being sent if we're not ready */ if (!sc->sc_running) { m_freem(m); return (ENETDOWN); } RSU_LOCK(sc); bf = rsu_getbuf(sc); if (bf == NULL) { m_freem(m); RSU_UNLOCK(sc); return (ENOBUFS); } if (rsu_tx_start(sc, ni, m, bf) != 0) { m_freem(m); rsu_freebuf(sc, bf); RSU_UNLOCK(sc); return (EIO); } RSU_UNLOCK(sc); return (0); } static void rsu_rxfilter_init(struct rsu_softc *sc) { uint32_t reg; RSU_ASSERT_LOCKED(sc); /* Setup multicast filter. */ rsu_set_multi(sc); /* Adjust Rx filter. */ reg = rsu_read_4(sc, R92S_RCR); reg &= ~R92S_RCR_AICV; reg |= R92S_RCR_APP_PHYSTS; if (sc->sc_rx_checksum_enable) reg |= R92S_RCR_TCP_OFFLD_EN; rsu_write_4(sc, R92S_RCR, reg); /* Update dynamic Rx filter parts. */ rsu_rxfilter_refresh(sc); } static void rsu_rxfilter_set(struct rsu_softc *sc, uint32_t clear, uint32_t set) { /* NB: firmware can touch this register too. */ rsu_write_4(sc, R92S_RCR, (rsu_read_4(sc, R92S_RCR) & ~clear) | set); } static void rsu_rxfilter_refresh(struct rsu_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t mask_all, mask_min; RSU_ASSERT_LOCKED(sc); /* NB: RCR_AMF / RXFLTMAP_MGT are used by firmware. */ mask_all = R92S_RCR_ACF | R92S_RCR_AAP; mask_min = R92S_RCR_APM; if (sc->sc_vap_is_running) mask_min |= R92S_RCR_CBSSID; else mask_all |= R92S_RCR_ADF; if (ic->ic_opmode == IEEE80211_M_MONITOR) { uint16_t rxfltmap; if (sc->sc_vap_is_running) rxfltmap = 0; else rxfltmap = R92S_RXFLTMAP_MGT_DEF; rsu_write_2(sc, R92S_RXFLTMAP_MGT, rxfltmap); } if (ic->ic_promisc == 0 && ic->ic_opmode != IEEE80211_M_MONITOR) rsu_rxfilter_set(sc, mask_all, mask_min); else rsu_rxfilter_set(sc, mask_min, mask_all); } static int rsu_init(struct rsu_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint8_t macaddr[IEEE80211_ADDR_LEN]; int error; int i; RSU_LOCK(sc); if (sc->sc_running) { RSU_UNLOCK(sc); return (0); } /* Ensure the mbuf queue is drained */ rsu_drain_mbufq(sc); /* Reset power management state. */ rsu_write_1(sc, R92S_USB_HRPWM, 0); /* Power on adapter. */ if (sc->cut == 1) rsu_power_on_acut(sc); else rsu_power_on_bcut(sc); /* Load firmware. */ error = rsu_load_firmware(sc); if (error != 0) goto fail; rsu_write_4(sc, R92S_CR, rsu_read_4(sc, R92S_CR) & ~0xff000000); /* Use 128 bytes pages. */ rsu_write_1(sc, 0x00b5, rsu_read_1(sc, 0x00b5) | 0x01); /* Enable USB Rx aggregation. */ rsu_write_1(sc, 0x00bd, rsu_read_1(sc, 0x00bd) | 0x80); /* Set USB Rx aggregation threshold. */ rsu_write_1(sc, 0x00d9, 0x01); /* Set USB Rx aggregation timeout (1.7ms/4). */ rsu_write_1(sc, 0xfe5b, 0x04); /* Fix USB Rx FIFO issue. */ rsu_write_1(sc, 0xfe5c, rsu_read_1(sc, 0xfe5c) | 0x80); /* Set MAC address. */ IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr); rsu_write_region_1(sc, R92S_MACID, macaddr, IEEE80211_ADDR_LEN); /* It really takes 1.5 seconds for the firmware to boot: */ usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(2000)); RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting MAC address to %s\n", __func__, ether_sprintf(macaddr)); error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, macaddr, IEEE80211_ADDR_LEN); if (error != 0) { device_printf(sc->sc_dev, "could not set MAC address\n"); goto fail; } /* Initialize Rx filter. */ rsu_rxfilter_init(sc); /* Set PS mode fully active */ error = rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); if (error != 0) { device_printf(sc->sc_dev, "could not set PS mode\n"); goto fail; } /* Install static keys (if any). */ error = rsu_reinit_static_keys(sc); if (error != 0) goto fail; sc->sc_extra_scan = 0; usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]); /* We're ready to go. */ sc->sc_running = 1; RSU_UNLOCK(sc); return (0); fail: /* Need to stop all failed transfers, if any */ for (i = 0; i != RSU_N_TRANSFER; i++) usbd_transfer_stop(sc->sc_xfer[i]); RSU_UNLOCK(sc); return (error); } static void rsu_stop(struct rsu_softc *sc) { int i; RSU_LOCK(sc); if (!sc->sc_running) { RSU_UNLOCK(sc); return; } sc->sc_running = 0; sc->sc_vap_is_running = 0; sc->sc_calibrating = 0; taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL); taskqueue_cancel(taskqueue_thread, &sc->tx_task, NULL); /* Power off adapter. */ rsu_power_off(sc); /* * CAM is not accessible after shutdown; * all entries are marked (by firmware?) as invalid. */ memset(sc->free_keys_bmap, 0, sizeof(sc->free_keys_bmap)); memset(sc->keys_bmap, 0, sizeof(sc->keys_bmap)); for (i = 0; i < RSU_N_TRANSFER; i++) usbd_transfer_stop(sc->sc_xfer[i]); /* Ensure the mbuf queue is drained */ rsu_drain_mbufq(sc); RSU_UNLOCK(sc); } /* * Note: usb_pause_mtx() actually releases the mutex before calling pause(), * which breaks any kind of driver serialisation. */ static void rsu_ms_delay(struct rsu_softc *sc, int ms) { //usb_pause_mtx(&sc->sc_mtx, hz / 1000); DELAY(ms * 1000); } Index: projects/clang700-import/sys/kern/kern_sig.c =================================================================== --- projects/clang700-import/sys/kern/kern_sig.c (revision 341812) +++ projects/clang700-import/sys/kern/kern_sig.c (revision 341813) @@ -1,3836 +1,3838 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_sig.c 8.7 (Berkeley) 4/18/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ONSIG 32 /* NSIG for osig* syscalls. XXX. */ SDT_PROVIDER_DECLARE(proc); SDT_PROBE_DEFINE3(proc, , , signal__send, "struct thread *", "struct proc *", "int"); SDT_PROBE_DEFINE2(proc, , , signal__clear, "int", "ksiginfo_t *"); SDT_PROBE_DEFINE3(proc, , , signal__discard, "struct thread *", "struct proc *", "int"); static int coredump(struct thread *); static int killpg1(struct thread *td, int sig, int pgid, int all, ksiginfo_t *ksi); static int issignal(struct thread *td); static int sigprop(int sig); static void tdsigwakeup(struct thread *, int, sig_t, int); static int sig_suspend_threads(struct thread *, struct proc *, int); static int filt_sigattach(struct knote *kn); static void filt_sigdetach(struct knote *kn); static int filt_signal(struct knote *kn, long hint); static struct thread *sigtd(struct proc *p, int sig, int prop); static void sigqueue_start(void); static uma_zone_t ksiginfo_zone = NULL; struct filterops sig_filtops = { .f_isfd = 0, .f_attach = filt_sigattach, .f_detach = filt_sigdetach, .f_event = filt_signal, }; static int kern_logsigexit = 1; SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW, &kern_logsigexit, 0, "Log processes quitting on abnormal signals to syslog(3)"); static int kern_forcesigexit = 1; SYSCTL_INT(_kern, OID_AUTO, forcesigexit, CTLFLAG_RW, &kern_forcesigexit, 0, "Force trap signal to be handled"); static SYSCTL_NODE(_kern, OID_AUTO, sigqueue, CTLFLAG_RW, 0, "POSIX real time signal"); static int max_pending_per_proc = 128; SYSCTL_INT(_kern_sigqueue, OID_AUTO, max_pending_per_proc, CTLFLAG_RW, &max_pending_per_proc, 0, "Max pending signals per proc"); static int preallocate_siginfo = 1024; SYSCTL_INT(_kern_sigqueue, OID_AUTO, preallocate, CTLFLAG_RDTUN, &preallocate_siginfo, 0, "Preallocated signal memory size"); static int signal_overflow = 0; SYSCTL_INT(_kern_sigqueue, OID_AUTO, overflow, CTLFLAG_RD, &signal_overflow, 0, "Number of signals overflew"); static int signal_alloc_fail = 0; SYSCTL_INT(_kern_sigqueue, OID_AUTO, alloc_fail, CTLFLAG_RD, &signal_alloc_fail, 0, "signals failed to be allocated"); static int kern_lognosys = 0; SYSCTL_INT(_kern, OID_AUTO, lognosys, CTLFLAG_RWTUN, &kern_lognosys, 0, "Log invalid syscalls"); SYSINIT(signal, SI_SUB_P1003_1B, SI_ORDER_FIRST+3, sigqueue_start, NULL); /* * Policy -- Can ucred cr1 send SIGIO to process cr2? * Should use cr_cansignal() once cr_cansignal() allows SIGIO and SIGURG * in the right situations. */ #define CANSIGIO(cr1, cr2) \ ((cr1)->cr_uid == 0 || \ (cr1)->cr_ruid == (cr2)->cr_ruid || \ (cr1)->cr_uid == (cr2)->cr_ruid || \ (cr1)->cr_ruid == (cr2)->cr_uid || \ (cr1)->cr_uid == (cr2)->cr_uid) static int sugid_coredump; SYSCTL_INT(_kern, OID_AUTO, sugid_coredump, CTLFLAG_RWTUN, &sugid_coredump, 0, "Allow setuid and setgid processes to dump core"); static int capmode_coredump; SYSCTL_INT(_kern, OID_AUTO, capmode_coredump, CTLFLAG_RWTUN, &capmode_coredump, 0, "Allow processes in capability mode to dump core"); static int do_coredump = 1; SYSCTL_INT(_kern, OID_AUTO, coredump, CTLFLAG_RW, &do_coredump, 0, "Enable/Disable coredumps"); static int set_core_nodump_flag = 0; SYSCTL_INT(_kern, OID_AUTO, nodump_coredump, CTLFLAG_RW, &set_core_nodump_flag, 0, "Enable setting the NODUMP flag on coredump files"); static int coredump_devctl = 0; SYSCTL_INT(_kern, OID_AUTO, coredump_devctl, CTLFLAG_RW, &coredump_devctl, 0, "Generate a devctl notification when processes coredump"); /* * Signal properties and actions. * The array below categorizes the signals and their default actions * according to the following properties: */ #define SIGPROP_KILL 0x01 /* terminates process by default */ #define SIGPROP_CORE 0x02 /* ditto and coredumps */ #define SIGPROP_STOP 0x04 /* suspend process */ #define SIGPROP_TTYSTOP 0x08 /* ditto, from tty */ #define SIGPROP_IGNORE 0x10 /* ignore by default */ #define SIGPROP_CONT 0x20 /* continue if suspended */ #define SIGPROP_CANTMASK 0x40 /* non-maskable, catchable */ static int sigproptbl[NSIG] = { [SIGHUP] = SIGPROP_KILL, [SIGINT] = SIGPROP_KILL, [SIGQUIT] = SIGPROP_KILL | SIGPROP_CORE, [SIGILL] = SIGPROP_KILL | SIGPROP_CORE, [SIGTRAP] = SIGPROP_KILL | SIGPROP_CORE, [SIGABRT] = SIGPROP_KILL | SIGPROP_CORE, [SIGEMT] = SIGPROP_KILL | SIGPROP_CORE, [SIGFPE] = SIGPROP_KILL | SIGPROP_CORE, [SIGKILL] = SIGPROP_KILL, [SIGBUS] = SIGPROP_KILL | SIGPROP_CORE, [SIGSEGV] = SIGPROP_KILL | SIGPROP_CORE, [SIGSYS] = SIGPROP_KILL | SIGPROP_CORE, [SIGPIPE] = SIGPROP_KILL, [SIGALRM] = SIGPROP_KILL, [SIGTERM] = SIGPROP_KILL, [SIGURG] = SIGPROP_IGNORE, [SIGSTOP] = SIGPROP_STOP, [SIGTSTP] = SIGPROP_STOP | SIGPROP_TTYSTOP, [SIGCONT] = SIGPROP_IGNORE | SIGPROP_CONT, [SIGCHLD] = SIGPROP_IGNORE, [SIGTTIN] = SIGPROP_STOP | SIGPROP_TTYSTOP, [SIGTTOU] = SIGPROP_STOP | SIGPROP_TTYSTOP, [SIGIO] = SIGPROP_IGNORE, [SIGXCPU] = SIGPROP_KILL, [SIGXFSZ] = SIGPROP_KILL, [SIGVTALRM] = SIGPROP_KILL, [SIGPROF] = SIGPROP_KILL, [SIGWINCH] = SIGPROP_IGNORE, [SIGINFO] = SIGPROP_IGNORE, [SIGUSR1] = SIGPROP_KILL, [SIGUSR2] = SIGPROP_KILL, }; static void reschedule_signals(struct proc *p, sigset_t block, int flags); static void sigqueue_start(void) { ksiginfo_zone = uma_zcreate("ksiginfo", sizeof(ksiginfo_t), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); uma_prealloc(ksiginfo_zone, preallocate_siginfo); p31b_setcfg(CTL_P1003_1B_REALTIME_SIGNALS, _POSIX_REALTIME_SIGNALS); p31b_setcfg(CTL_P1003_1B_RTSIG_MAX, SIGRTMAX - SIGRTMIN + 1); p31b_setcfg(CTL_P1003_1B_SIGQUEUE_MAX, max_pending_per_proc); } ksiginfo_t * ksiginfo_alloc(int wait) { int flags; flags = M_ZERO; if (! wait) flags |= M_NOWAIT; if (ksiginfo_zone != NULL) return ((ksiginfo_t *)uma_zalloc(ksiginfo_zone, flags)); return (NULL); } void ksiginfo_free(ksiginfo_t *ksi) { uma_zfree(ksiginfo_zone, ksi); } static __inline int ksiginfo_tryfree(ksiginfo_t *ksi) { if (!(ksi->ksi_flags & KSI_EXT)) { uma_zfree(ksiginfo_zone, ksi); return (1); } return (0); } void sigqueue_init(sigqueue_t *list, struct proc *p) { SIGEMPTYSET(list->sq_signals); SIGEMPTYSET(list->sq_kill); SIGEMPTYSET(list->sq_ptrace); TAILQ_INIT(&list->sq_list); list->sq_proc = p; list->sq_flags = SQ_INIT; } /* * Get a signal's ksiginfo. * Return: * 0 - signal not found * others - signal number */ static int sigqueue_get(sigqueue_t *sq, int signo, ksiginfo_t *si) { struct proc *p = sq->sq_proc; struct ksiginfo *ksi, *next; int count = 0; KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); if (!SIGISMEMBER(sq->sq_signals, signo)) return (0); if (SIGISMEMBER(sq->sq_ptrace, signo)) { count++; SIGDELSET(sq->sq_ptrace, signo); si->ksi_flags |= KSI_PTRACE; } if (SIGISMEMBER(sq->sq_kill, signo)) { count++; if (count == 1) SIGDELSET(sq->sq_kill, signo); } TAILQ_FOREACH_SAFE(ksi, &sq->sq_list, ksi_link, next) { if (ksi->ksi_signo == signo) { if (count == 0) { TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = NULL; ksiginfo_copy(ksi, si); if (ksiginfo_tryfree(ksi) && p != NULL) p->p_pendingcnt--; } if (++count > 1) break; } } if (count <= 1) SIGDELSET(sq->sq_signals, signo); si->ksi_signo = signo; return (signo); } void sigqueue_take(ksiginfo_t *ksi) { struct ksiginfo *kp; struct proc *p; sigqueue_t *sq; if (ksi == NULL || (sq = ksi->ksi_sigq) == NULL) return; p = sq->sq_proc; TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = NULL; if (!(ksi->ksi_flags & KSI_EXT) && p != NULL) p->p_pendingcnt--; for (kp = TAILQ_FIRST(&sq->sq_list); kp != NULL; kp = TAILQ_NEXT(kp, ksi_link)) { if (kp->ksi_signo == ksi->ksi_signo) break; } if (kp == NULL && !SIGISMEMBER(sq->sq_kill, ksi->ksi_signo) && !SIGISMEMBER(sq->sq_ptrace, ksi->ksi_signo)) SIGDELSET(sq->sq_signals, ksi->ksi_signo); } static int sigqueue_add(sigqueue_t *sq, int signo, ksiginfo_t *si) { struct proc *p = sq->sq_proc; struct ksiginfo *ksi; int ret = 0; KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); /* * SIGKILL/SIGSTOP cannot be caught or masked, so take the fast path * for these signals. */ if (signo == SIGKILL || signo == SIGSTOP || si == NULL) { SIGADDSET(sq->sq_kill, signo); goto out_set_bit; } /* directly insert the ksi, don't copy it */ if (si->ksi_flags & KSI_INS) { if (si->ksi_flags & KSI_HEAD) TAILQ_INSERT_HEAD(&sq->sq_list, si, ksi_link); else TAILQ_INSERT_TAIL(&sq->sq_list, si, ksi_link); si->ksi_sigq = sq; goto out_set_bit; } if (__predict_false(ksiginfo_zone == NULL)) { SIGADDSET(sq->sq_kill, signo); goto out_set_bit; } if (p != NULL && p->p_pendingcnt >= max_pending_per_proc) { signal_overflow++; ret = EAGAIN; } else if ((ksi = ksiginfo_alloc(0)) == NULL) { signal_alloc_fail++; ret = EAGAIN; } else { if (p != NULL) p->p_pendingcnt++; ksiginfo_copy(si, ksi); ksi->ksi_signo = signo; if (si->ksi_flags & KSI_HEAD) TAILQ_INSERT_HEAD(&sq->sq_list, ksi, ksi_link); else TAILQ_INSERT_TAIL(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = sq; } if (ret != 0) { if ((si->ksi_flags & KSI_PTRACE) != 0) { SIGADDSET(sq->sq_ptrace, signo); ret = 0; goto out_set_bit; } else if ((si->ksi_flags & KSI_TRAP) != 0 || (si->ksi_flags & KSI_SIGQ) == 0) { SIGADDSET(sq->sq_kill, signo); ret = 0; goto out_set_bit; } return (ret); } out_set_bit: SIGADDSET(sq->sq_signals, signo); return (ret); } void sigqueue_flush(sigqueue_t *sq) { struct proc *p = sq->sq_proc; ksiginfo_t *ksi; KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); while ((ksi = TAILQ_FIRST(&sq->sq_list)) != NULL) { TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = NULL; if (ksiginfo_tryfree(ksi) && p != NULL) p->p_pendingcnt--; } SIGEMPTYSET(sq->sq_signals); SIGEMPTYSET(sq->sq_kill); SIGEMPTYSET(sq->sq_ptrace); } static void sigqueue_move_set(sigqueue_t *src, sigqueue_t *dst, const sigset_t *set) { sigset_t tmp; struct proc *p1, *p2; ksiginfo_t *ksi, *next; KASSERT(src->sq_flags & SQ_INIT, ("src sigqueue not inited")); KASSERT(dst->sq_flags & SQ_INIT, ("dst sigqueue not inited")); p1 = src->sq_proc; p2 = dst->sq_proc; /* Move siginfo to target list */ TAILQ_FOREACH_SAFE(ksi, &src->sq_list, ksi_link, next) { if (SIGISMEMBER(*set, ksi->ksi_signo)) { TAILQ_REMOVE(&src->sq_list, ksi, ksi_link); if (p1 != NULL) p1->p_pendingcnt--; TAILQ_INSERT_TAIL(&dst->sq_list, ksi, ksi_link); ksi->ksi_sigq = dst; if (p2 != NULL) p2->p_pendingcnt++; } } /* Move pending bits to target list */ tmp = src->sq_kill; SIGSETAND(tmp, *set); SIGSETOR(dst->sq_kill, tmp); SIGSETNAND(src->sq_kill, tmp); tmp = src->sq_ptrace; SIGSETAND(tmp, *set); SIGSETOR(dst->sq_ptrace, tmp); SIGSETNAND(src->sq_ptrace, tmp); tmp = src->sq_signals; SIGSETAND(tmp, *set); SIGSETOR(dst->sq_signals, tmp); SIGSETNAND(src->sq_signals, tmp); } #if 0 static void sigqueue_move(sigqueue_t *src, sigqueue_t *dst, int signo) { sigset_t set; SIGEMPTYSET(set); SIGADDSET(set, signo); sigqueue_move_set(src, dst, &set); } #endif static void sigqueue_delete_set(sigqueue_t *sq, const sigset_t *set) { struct proc *p = sq->sq_proc; ksiginfo_t *ksi, *next; KASSERT(sq->sq_flags & SQ_INIT, ("src sigqueue not inited")); /* Remove siginfo queue */ TAILQ_FOREACH_SAFE(ksi, &sq->sq_list, ksi_link, next) { if (SIGISMEMBER(*set, ksi->ksi_signo)) { TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = NULL; if (ksiginfo_tryfree(ksi) && p != NULL) p->p_pendingcnt--; } } SIGSETNAND(sq->sq_kill, *set); SIGSETNAND(sq->sq_ptrace, *set); SIGSETNAND(sq->sq_signals, *set); } void sigqueue_delete(sigqueue_t *sq, int signo) { sigset_t set; SIGEMPTYSET(set); SIGADDSET(set, signo); sigqueue_delete_set(sq, &set); } /* Remove a set of signals for a process */ static void sigqueue_delete_set_proc(struct proc *p, const sigset_t *set) { sigqueue_t worklist; struct thread *td0; PROC_LOCK_ASSERT(p, MA_OWNED); sigqueue_init(&worklist, NULL); sigqueue_move_set(&p->p_sigqueue, &worklist, set); FOREACH_THREAD_IN_PROC(p, td0) sigqueue_move_set(&td0->td_sigqueue, &worklist, set); sigqueue_flush(&worklist); } void sigqueue_delete_proc(struct proc *p, int signo) { sigset_t set; SIGEMPTYSET(set); SIGADDSET(set, signo); sigqueue_delete_set_proc(p, &set); } static void sigqueue_delete_stopmask_proc(struct proc *p) { sigset_t set; SIGEMPTYSET(set); SIGADDSET(set, SIGSTOP); SIGADDSET(set, SIGTSTP); SIGADDSET(set, SIGTTIN); SIGADDSET(set, SIGTTOU); sigqueue_delete_set_proc(p, &set); } /* * Determine signal that should be delivered to thread td, the current * thread, 0 if none. If there is a pending stop signal with default * action, the process stops in issignal(). */ int cursig(struct thread *td) { PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); mtx_assert(&td->td_proc->p_sigacts->ps_mtx, MA_OWNED); THREAD_LOCK_ASSERT(td, MA_NOTOWNED); return (SIGPENDING(td) ? issignal(td) : 0); } /* * Arrange for ast() to handle unmasked pending signals on return to user * mode. This must be called whenever a signal is added to td_sigqueue or * unmasked in td_sigmask. */ void signotify(struct thread *td) { PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); if (SIGPENDING(td)) { thread_lock(td); td->td_flags |= TDF_NEEDSIGCHK | TDF_ASTPENDING; thread_unlock(td); } } /* * Returns 1 (true) if altstack is configured for the thread, and the * passed stack bottom address falls into the altstack range. Handles * the 43 compat special case where the alt stack size is zero. */ int sigonstack(size_t sp) { struct thread *td; td = curthread; if ((td->td_pflags & TDP_ALTSTACK) == 0) return (0); #if defined(COMPAT_43) if (td->td_sigstk.ss_size == 0) return ((td->td_sigstk.ss_flags & SS_ONSTACK) != 0); #endif return (sp >= (size_t)td->td_sigstk.ss_sp && sp < td->td_sigstk.ss_size + (size_t)td->td_sigstk.ss_sp); } static __inline int sigprop(int sig) { if (sig > 0 && sig < nitems(sigproptbl)) return (sigproptbl[sig]); return (0); } int sig_ffs(sigset_t *set) { int i; for (i = 0; i < _SIG_WORDS; i++) if (set->__bits[i]) return (ffs(set->__bits[i]) + (i * 32)); return (0); } static bool sigact_flag_test(const struct sigaction *act, int flag) { /* * SA_SIGINFO is reset when signal disposition is set to * ignore or default. Other flags are kept according to user * settings. */ return ((act->sa_flags & flag) != 0 && (flag != SA_SIGINFO || ((__sighandler_t *)act->sa_sigaction != SIG_IGN && (__sighandler_t *)act->sa_sigaction != SIG_DFL))); } /* * kern_sigaction * sigaction * freebsd4_sigaction * osigaction */ int kern_sigaction(struct thread *td, int sig, const struct sigaction *act, struct sigaction *oact, int flags) { struct sigacts *ps; struct proc *p = td->td_proc; if (!_SIG_VALID(sig)) return (EINVAL); if (act != NULL && act->sa_handler != SIG_DFL && act->sa_handler != SIG_IGN && (act->sa_flags & ~(SA_ONSTACK | SA_RESTART | SA_RESETHAND | SA_NOCLDSTOP | SA_NODEFER | SA_NOCLDWAIT | SA_SIGINFO)) != 0) return (EINVAL); PROC_LOCK(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); if (oact) { memset(oact, 0, sizeof(*oact)); oact->sa_mask = ps->ps_catchmask[_SIG_IDX(sig)]; if (SIGISMEMBER(ps->ps_sigonstack, sig)) oact->sa_flags |= SA_ONSTACK; if (!SIGISMEMBER(ps->ps_sigintr, sig)) oact->sa_flags |= SA_RESTART; if (SIGISMEMBER(ps->ps_sigreset, sig)) oact->sa_flags |= SA_RESETHAND; if (SIGISMEMBER(ps->ps_signodefer, sig)) oact->sa_flags |= SA_NODEFER; if (SIGISMEMBER(ps->ps_siginfo, sig)) { oact->sa_flags |= SA_SIGINFO; oact->sa_sigaction = (__siginfohandler_t *)ps->ps_sigact[_SIG_IDX(sig)]; } else oact->sa_handler = ps->ps_sigact[_SIG_IDX(sig)]; if (sig == SIGCHLD && ps->ps_flag & PS_NOCLDSTOP) oact->sa_flags |= SA_NOCLDSTOP; if (sig == SIGCHLD && ps->ps_flag & PS_NOCLDWAIT) oact->sa_flags |= SA_NOCLDWAIT; } if (act) { if ((sig == SIGKILL || sig == SIGSTOP) && act->sa_handler != SIG_DFL) { mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); return (EINVAL); } /* * Change setting atomically. */ ps->ps_catchmask[_SIG_IDX(sig)] = act->sa_mask; SIG_CANTMASK(ps->ps_catchmask[_SIG_IDX(sig)]); if (sigact_flag_test(act, SA_SIGINFO)) { ps->ps_sigact[_SIG_IDX(sig)] = (__sighandler_t *)act->sa_sigaction; SIGADDSET(ps->ps_siginfo, sig); } else { ps->ps_sigact[_SIG_IDX(sig)] = act->sa_handler; SIGDELSET(ps->ps_siginfo, sig); } if (!sigact_flag_test(act, SA_RESTART)) SIGADDSET(ps->ps_sigintr, sig); else SIGDELSET(ps->ps_sigintr, sig); if (sigact_flag_test(act, SA_ONSTACK)) SIGADDSET(ps->ps_sigonstack, sig); else SIGDELSET(ps->ps_sigonstack, sig); if (sigact_flag_test(act, SA_RESETHAND)) SIGADDSET(ps->ps_sigreset, sig); else SIGDELSET(ps->ps_sigreset, sig); if (sigact_flag_test(act, SA_NODEFER)) SIGADDSET(ps->ps_signodefer, sig); else SIGDELSET(ps->ps_signodefer, sig); if (sig == SIGCHLD) { if (act->sa_flags & SA_NOCLDSTOP) ps->ps_flag |= PS_NOCLDSTOP; else ps->ps_flag &= ~PS_NOCLDSTOP; if (act->sa_flags & SA_NOCLDWAIT) { /* * Paranoia: since SA_NOCLDWAIT is implemented * by reparenting the dying child to PID 1 (and * trust it to reap the zombie), PID 1 itself * is forbidden to set SA_NOCLDWAIT. */ if (p->p_pid == 1) ps->ps_flag &= ~PS_NOCLDWAIT; else ps->ps_flag |= PS_NOCLDWAIT; } else ps->ps_flag &= ~PS_NOCLDWAIT; if (ps->ps_sigact[_SIG_IDX(SIGCHLD)] == SIG_IGN) ps->ps_flag |= PS_CLDSIGIGN; else ps->ps_flag &= ~PS_CLDSIGIGN; } /* * Set bit in ps_sigignore for signals that are set to SIG_IGN, * and for signals set to SIG_DFL where the default is to * ignore. However, don't put SIGCONT in ps_sigignore, as we * have to restart the process. */ if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || (sigprop(sig) & SIGPROP_IGNORE && ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL)) { /* never to be seen again */ sigqueue_delete_proc(p, sig); if (sig != SIGCONT) /* easier in psignal */ SIGADDSET(ps->ps_sigignore, sig); SIGDELSET(ps->ps_sigcatch, sig); } else { SIGDELSET(ps->ps_sigignore, sig); if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL) SIGDELSET(ps->ps_sigcatch, sig); else SIGADDSET(ps->ps_sigcatch, sig); } #ifdef COMPAT_FREEBSD4 if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL || (flags & KSA_FREEBSD4) == 0) SIGDELSET(ps->ps_freebsd4, sig); else SIGADDSET(ps->ps_freebsd4, sig); #endif #ifdef COMPAT_43 if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL || (flags & KSA_OSIGSET) == 0) SIGDELSET(ps->ps_osigset, sig); else SIGADDSET(ps->ps_osigset, sig); #endif } mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); return (0); } #ifndef _SYS_SYSPROTO_H_ struct sigaction_args { int sig; struct sigaction *act; struct sigaction *oact; }; #endif int sys_sigaction(struct thread *td, struct sigaction_args *uap) { struct sigaction act, oact; struct sigaction *actp, *oactp; int error; actp = (uap->act != NULL) ? &act : NULL; oactp = (uap->oact != NULL) ? &oact : NULL; if (actp) { error = copyin(uap->act, actp, sizeof(act)); if (error) return (error); } error = kern_sigaction(td, uap->sig, actp, oactp, 0); if (oactp && !error) error = copyout(oactp, uap->oact, sizeof(oact)); return (error); } #ifdef COMPAT_FREEBSD4 #ifndef _SYS_SYSPROTO_H_ struct freebsd4_sigaction_args { int sig; struct sigaction *act; struct sigaction *oact; }; #endif int freebsd4_sigaction(struct thread *td, struct freebsd4_sigaction_args *uap) { struct sigaction act, oact; struct sigaction *actp, *oactp; int error; actp = (uap->act != NULL) ? &act : NULL; oactp = (uap->oact != NULL) ? &oact : NULL; if (actp) { error = copyin(uap->act, actp, sizeof(act)); if (error) return (error); } error = kern_sigaction(td, uap->sig, actp, oactp, KSA_FREEBSD4); if (oactp && !error) error = copyout(oactp, uap->oact, sizeof(oact)); return (error); } #endif /* COMAPT_FREEBSD4 */ #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ #ifndef _SYS_SYSPROTO_H_ struct osigaction_args { int signum; struct osigaction *nsa; struct osigaction *osa; }; #endif int osigaction(struct thread *td, struct osigaction_args *uap) { struct osigaction sa; struct sigaction nsa, osa; struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsa != NULL) ? &nsa : NULL; osap = (uap->osa != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsa, &sa, sizeof(sa)); if (error) return (error); nsap->sa_handler = sa.sa_handler; nsap->sa_flags = sa.sa_flags; OSIG2SIG(sa.sa_mask, nsap->sa_mask); } error = kern_sigaction(td, uap->signum, nsap, osap, KSA_OSIGSET); if (osap && !error) { sa.sa_handler = osap->sa_handler; sa.sa_flags = osap->sa_flags; SIG2OSIG(osap->sa_mask, sa.sa_mask); error = copyout(&sa, uap->osa, sizeof(sa)); } return (error); } #if !defined(__i386__) /* Avoid replicating the same stub everywhere */ int osigreturn(struct thread *td, struct osigreturn_args *uap) { return (nosys(td, (struct nosys_args *)uap)); } #endif #endif /* COMPAT_43 */ /* * Initialize signal state for process 0; * set to ignore signals that are ignored by default. */ void siginit(struct proc *p) { int i; struct sigacts *ps; PROC_LOCK(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); for (i = 1; i <= NSIG; i++) { if (sigprop(i) & SIGPROP_IGNORE && i != SIGCONT) { SIGADDSET(ps->ps_sigignore, i); } } mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); } /* * Reset specified signal to the default disposition. */ static void sigdflt(struct sigacts *ps, int sig) { mtx_assert(&ps->ps_mtx, MA_OWNED); SIGDELSET(ps->ps_sigcatch, sig); if ((sigprop(sig) & SIGPROP_IGNORE) != 0 && sig != SIGCONT) SIGADDSET(ps->ps_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; SIGDELSET(ps->ps_siginfo, sig); } /* * Reset signals for an exec of the specified process. */ void execsigs(struct proc *p) { sigset_t osigignore; struct sigacts *ps; int sig; struct thread *td; /* * Reset caught signals. Held signals remain held * through td_sigmask (unless they were caught, * and are now ignored by default). */ PROC_LOCK_ASSERT(p, MA_OWNED); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); while (SIGNOTEMPTY(ps->ps_sigcatch)) { sig = sig_ffs(&ps->ps_sigcatch); sigdflt(ps, sig); if ((sigprop(sig) & SIGPROP_IGNORE) != 0) sigqueue_delete_proc(p, sig); } /* * As CloudABI processes cannot modify signal handlers, fully * reset all signals to their default behavior. Do ignore * SIGPIPE, as it would otherwise be impossible to recover from * writes to broken pipes and sockets. */ if (SV_PROC_ABI(p) == SV_ABI_CLOUDABI) { osigignore = ps->ps_sigignore; while (SIGNOTEMPTY(osigignore)) { sig = sig_ffs(&osigignore); SIGDELSET(osigignore, sig); if (sig != SIGPIPE) sigdflt(ps, sig); } SIGADDSET(ps->ps_sigignore, SIGPIPE); } /* * Reset stack state to the user stack. * Clear set of signals caught on the signal stack. */ td = curthread; MPASS(td->td_proc == p); td->td_sigstk.ss_flags = SS_DISABLE; td->td_sigstk.ss_size = 0; td->td_sigstk.ss_sp = 0; td->td_pflags &= ~TDP_ALTSTACK; /* * Reset no zombies if child dies flag as Solaris does. */ ps->ps_flag &= ~(PS_NOCLDWAIT | PS_CLDSIGIGN); if (ps->ps_sigact[_SIG_IDX(SIGCHLD)] == SIG_IGN) ps->ps_sigact[_SIG_IDX(SIGCHLD)] = SIG_DFL; mtx_unlock(&ps->ps_mtx); } /* * kern_sigprocmask() * * Manipulate signal mask. */ int kern_sigprocmask(struct thread *td, int how, sigset_t *set, sigset_t *oset, int flags) { sigset_t new_block, oset1; struct proc *p; int error; p = td->td_proc; if ((flags & SIGPROCMASK_PROC_LOCKED) != 0) PROC_LOCK_ASSERT(p, MA_OWNED); else PROC_LOCK(p); mtx_assert(&p->p_sigacts->ps_mtx, (flags & SIGPROCMASK_PS_LOCKED) != 0 ? MA_OWNED : MA_NOTOWNED); if (oset != NULL) *oset = td->td_sigmask; error = 0; if (set != NULL) { switch (how) { case SIG_BLOCK: SIG_CANTMASK(*set); oset1 = td->td_sigmask; SIGSETOR(td->td_sigmask, *set); new_block = td->td_sigmask; SIGSETNAND(new_block, oset1); break; case SIG_UNBLOCK: SIGSETNAND(td->td_sigmask, *set); signotify(td); goto out; case SIG_SETMASK: SIG_CANTMASK(*set); oset1 = td->td_sigmask; if (flags & SIGPROCMASK_OLD) SIGSETLO(td->td_sigmask, *set); else td->td_sigmask = *set; new_block = td->td_sigmask; SIGSETNAND(new_block, oset1); signotify(td); break; default: error = EINVAL; goto out; } /* * The new_block set contains signals that were not previously * blocked, but are blocked now. * * In case we block any signal that was not previously blocked * for td, and process has the signal pending, try to schedule * signal delivery to some thread that does not block the * signal, possibly waking it up. */ if (p->p_numthreads != 1) reschedule_signals(p, new_block, flags); } out: if (!(flags & SIGPROCMASK_PROC_LOCKED)) PROC_UNLOCK(p); return (error); } #ifndef _SYS_SYSPROTO_H_ struct sigprocmask_args { int how; const sigset_t *set; sigset_t *oset; }; #endif int sys_sigprocmask(struct thread *td, struct sigprocmask_args *uap) { sigset_t set, oset; sigset_t *setp, *osetp; int error; setp = (uap->set != NULL) ? &set : NULL; osetp = (uap->oset != NULL) ? &oset : NULL; if (setp) { error = copyin(uap->set, setp, sizeof(set)); if (error) return (error); } error = kern_sigprocmask(td, uap->how, setp, osetp, 0); if (osetp && !error) { error = copyout(osetp, uap->oset, sizeof(oset)); } return (error); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ #ifndef _SYS_SYSPROTO_H_ struct osigprocmask_args { int how; osigset_t mask; }; #endif int osigprocmask(struct thread *td, struct osigprocmask_args *uap) { sigset_t set, oset; int error; OSIG2SIG(uap->mask, set); error = kern_sigprocmask(td, uap->how, &set, &oset, 1); SIG2OSIG(oset, td->td_retval[0]); return (error); } #endif /* COMPAT_43 */ int sys_sigwait(struct thread *td, struct sigwait_args *uap) { ksiginfo_t ksi; sigset_t set; int error; error = copyin(uap->set, &set, sizeof(set)); if (error) { td->td_retval[0] = error; return (0); } error = kern_sigtimedwait(td, set, &ksi, NULL); if (error) { if (error == EINTR && td->td_proc->p_osrel < P_OSREL_SIGWAIT) error = ERESTART; if (error == ERESTART) return (error); td->td_retval[0] = error; return (0); } error = copyout(&ksi.ksi_signo, uap->sig, sizeof(ksi.ksi_signo)); td->td_retval[0] = error; return (0); } int sys_sigtimedwait(struct thread *td, struct sigtimedwait_args *uap) { struct timespec ts; struct timespec *timeout; sigset_t set; ksiginfo_t ksi; int error; if (uap->timeout) { error = copyin(uap->timeout, &ts, sizeof(ts)); if (error) return (error); timeout = &ts; } else timeout = NULL; error = copyin(uap->set, &set, sizeof(set)); if (error) return (error); error = kern_sigtimedwait(td, set, &ksi, timeout); if (error) return (error); if (uap->info) error = copyout(&ksi.ksi_info, uap->info, sizeof(siginfo_t)); if (error == 0) td->td_retval[0] = ksi.ksi_signo; return (error); } int sys_sigwaitinfo(struct thread *td, struct sigwaitinfo_args *uap) { ksiginfo_t ksi; sigset_t set; int error; error = copyin(uap->set, &set, sizeof(set)); if (error) return (error); error = kern_sigtimedwait(td, set, &ksi, NULL); if (error) return (error); if (uap->info) error = copyout(&ksi.ksi_info, uap->info, sizeof(siginfo_t)); if (error == 0) td->td_retval[0] = ksi.ksi_signo; return (error); } static void proc_td_siginfo_capture(struct thread *td, siginfo_t *si) { struct thread *thr; FOREACH_THREAD_IN_PROC(td->td_proc, thr) { if (thr == td) thr->td_si = *si; else thr->td_si.si_signo = 0; } } int kern_sigtimedwait(struct thread *td, sigset_t waitset, ksiginfo_t *ksi, struct timespec *timeout) { struct sigacts *ps; sigset_t saved_mask, new_block; struct proc *p; int error, sig, timo, timevalid = 0; struct timespec rts, ets, ts; struct timeval tv; p = td->td_proc; error = 0; ets.tv_sec = 0; ets.tv_nsec = 0; if (timeout != NULL) { if (timeout->tv_nsec >= 0 && timeout->tv_nsec < 1000000000) { timevalid = 1; getnanouptime(&rts); timespecadd(&rts, timeout, &ets); } } ksiginfo_init(ksi); /* Some signals can not be waited for. */ SIG_CANTMASK(waitset); ps = p->p_sigacts; PROC_LOCK(p); saved_mask = td->td_sigmask; SIGSETNAND(td->td_sigmask, waitset); for (;;) { mtx_lock(&ps->ps_mtx); sig = cursig(td); mtx_unlock(&ps->ps_mtx); KASSERT(sig >= 0, ("sig %d", sig)); if (sig != 0 && SIGISMEMBER(waitset, sig)) { if (sigqueue_get(&td->td_sigqueue, sig, ksi) != 0 || sigqueue_get(&p->p_sigqueue, sig, ksi) != 0) { error = 0; break; } } if (error != 0) break; /* * POSIX says this must be checked after looking for pending * signals. */ if (timeout != NULL) { if (!timevalid) { error = EINVAL; break; } getnanouptime(&rts); if (timespeccmp(&rts, &ets, >=)) { error = EAGAIN; break; } timespecsub(&ets, &rts, &ts); TIMESPEC_TO_TIMEVAL(&tv, &ts); timo = tvtohz(&tv); } else { timo = 0; } error = msleep(ps, &p->p_mtx, PPAUSE|PCATCH, "sigwait", timo); if (timeout != NULL) { if (error == ERESTART) { /* Timeout can not be restarted. */ error = EINTR; } else if (error == EAGAIN) { /* We will calculate timeout by ourself. */ error = 0; } } } new_block = saved_mask; SIGSETNAND(new_block, td->td_sigmask); td->td_sigmask = saved_mask; /* * Fewer signals can be delivered to us, reschedule signal * notification. */ if (p->p_numthreads != 1) reschedule_signals(p, new_block, 0); if (error == 0) { SDT_PROBE2(proc, , , signal__clear, sig, ksi); if (ksi->ksi_code == SI_TIMER) itimer_accept(p, ksi->ksi_timerid, ksi); #ifdef KTRACE if (KTRPOINT(td, KTR_PSIG)) { sig_t action; mtx_lock(&ps->ps_mtx); action = ps->ps_sigact[_SIG_IDX(sig)]; mtx_unlock(&ps->ps_mtx); ktrpsig(sig, action, &td->td_sigmask, ksi->ksi_code); } #endif if (sig == SIGKILL) { proc_td_siginfo_capture(td, &ksi->ksi_info); sigexit(td, sig); } } PROC_UNLOCK(p); return (error); } #ifndef _SYS_SYSPROTO_H_ struct sigpending_args { sigset_t *set; }; #endif int sys_sigpending(struct thread *td, struct sigpending_args *uap) { struct proc *p = td->td_proc; sigset_t pending; PROC_LOCK(p); pending = p->p_sigqueue.sq_signals; SIGSETOR(pending, td->td_sigqueue.sq_signals); PROC_UNLOCK(p); return (copyout(&pending, uap->set, sizeof(sigset_t))); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ #ifndef _SYS_SYSPROTO_H_ struct osigpending_args { int dummy; }; #endif int osigpending(struct thread *td, struct osigpending_args *uap) { struct proc *p = td->td_proc; sigset_t pending; PROC_LOCK(p); pending = p->p_sigqueue.sq_signals; SIGSETOR(pending, td->td_sigqueue.sq_signals); PROC_UNLOCK(p); SIG2OSIG(pending, td->td_retval[0]); return (0); } #endif /* COMPAT_43 */ #if defined(COMPAT_43) /* * Generalized interface signal handler, 4.3-compatible. */ #ifndef _SYS_SYSPROTO_H_ struct osigvec_args { int signum; struct sigvec *nsv; struct sigvec *osv; }; #endif /* ARGSUSED */ int osigvec(struct thread *td, struct osigvec_args *uap) { struct sigvec vec; struct sigaction nsa, osa; struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsv != NULL) ? &nsa : NULL; osap = (uap->osv != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsv, &vec, sizeof(vec)); if (error) return (error); nsap->sa_handler = vec.sv_handler; OSIG2SIG(vec.sv_mask, nsap->sa_mask); nsap->sa_flags = vec.sv_flags; nsap->sa_flags ^= SA_RESTART; /* opposite of SV_INTERRUPT */ } error = kern_sigaction(td, uap->signum, nsap, osap, KSA_OSIGSET); if (osap && !error) { vec.sv_handler = osap->sa_handler; SIG2OSIG(osap->sa_mask, vec.sv_mask); vec.sv_flags = osap->sa_flags; vec.sv_flags &= ~SA_NOCLDWAIT; vec.sv_flags ^= SA_RESTART; error = copyout(&vec, uap->osv, sizeof(vec)); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct osigblock_args { int mask; }; #endif int osigblock(struct thread *td, struct osigblock_args *uap) { sigset_t set, oset; OSIG2SIG(uap->mask, set); kern_sigprocmask(td, SIG_BLOCK, &set, &oset, 0); SIG2OSIG(oset, td->td_retval[0]); return (0); } #ifndef _SYS_SYSPROTO_H_ struct osigsetmask_args { int mask; }; #endif int osigsetmask(struct thread *td, struct osigsetmask_args *uap) { sigset_t set, oset; OSIG2SIG(uap->mask, set); kern_sigprocmask(td, SIG_SETMASK, &set, &oset, 0); SIG2OSIG(oset, td->td_retval[0]); return (0); } #endif /* COMPAT_43 */ /* * Suspend calling thread until signal, providing mask to be set in the * meantime. */ #ifndef _SYS_SYSPROTO_H_ struct sigsuspend_args { const sigset_t *sigmask; }; #endif /* ARGSUSED */ int sys_sigsuspend(struct thread *td, struct sigsuspend_args *uap) { sigset_t mask; int error; error = copyin(uap->sigmask, &mask, sizeof(mask)); if (error) return (error); return (kern_sigsuspend(td, mask)); } int kern_sigsuspend(struct thread *td, sigset_t mask) { struct proc *p = td->td_proc; int has_sig, sig; /* * When returning from sigsuspend, we want * the old mask to be restored after the * signal handler has finished. Thus, we * save it here and mark the sigacts structure * to indicate this. */ PROC_LOCK(p); kern_sigprocmask(td, SIG_SETMASK, &mask, &td->td_oldsigmask, SIGPROCMASK_PROC_LOCKED); td->td_pflags |= TDP_OLDMASK; /* * Process signals now. Otherwise, we can get spurious wakeup * due to signal entered process queue, but delivered to other * thread. But sigsuspend should return only on signal * delivery. */ (p->p_sysent->sv_set_syscall_retval)(td, EINTR); for (has_sig = 0; !has_sig;) { while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "pause", 0) == 0) /* void */; thread_suspend_check(0); mtx_lock(&p->p_sigacts->ps_mtx); while ((sig = cursig(td)) != 0) { KASSERT(sig >= 0, ("sig %d", sig)); has_sig += postsig(sig); } mtx_unlock(&p->p_sigacts->ps_mtx); } PROC_UNLOCK(p); td->td_errno = EINTR; td->td_pflags |= TDP_NERRNO; return (EJUSTRETURN); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ /* * Compatibility sigsuspend call for old binaries. Note nonstandard calling * convention: libc stub passes mask, not pointer, to save a copyin. */ #ifndef _SYS_SYSPROTO_H_ struct osigsuspend_args { osigset_t mask; }; #endif /* ARGSUSED */ int osigsuspend(struct thread *td, struct osigsuspend_args *uap) { sigset_t mask; OSIG2SIG(uap->mask, mask); return (kern_sigsuspend(td, mask)); } #endif /* COMPAT_43 */ #if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct osigstack_args { struct sigstack *nss; struct sigstack *oss; }; #endif /* ARGSUSED */ int osigstack(struct thread *td, struct osigstack_args *uap) { struct sigstack nss, oss; int error = 0; if (uap->nss != NULL) { error = copyin(uap->nss, &nss, sizeof(nss)); if (error) return (error); } oss.ss_sp = td->td_sigstk.ss_sp; oss.ss_onstack = sigonstack(cpu_getstack(td)); if (uap->nss != NULL) { td->td_sigstk.ss_sp = nss.ss_sp; td->td_sigstk.ss_size = 0; td->td_sigstk.ss_flags |= nss.ss_onstack & SS_ONSTACK; td->td_pflags |= TDP_ALTSTACK; } if (uap->oss != NULL) error = copyout(&oss, uap->oss, sizeof(oss)); return (error); } #endif /* COMPAT_43 */ #ifndef _SYS_SYSPROTO_H_ struct sigaltstack_args { stack_t *ss; stack_t *oss; }; #endif /* ARGSUSED */ int sys_sigaltstack(struct thread *td, struct sigaltstack_args *uap) { stack_t ss, oss; int error; if (uap->ss != NULL) { error = copyin(uap->ss, &ss, sizeof(ss)); if (error) return (error); } error = kern_sigaltstack(td, (uap->ss != NULL) ? &ss : NULL, (uap->oss != NULL) ? &oss : NULL); if (error) return (error); if (uap->oss != NULL) error = copyout(&oss, uap->oss, sizeof(stack_t)); return (error); } int kern_sigaltstack(struct thread *td, stack_t *ss, stack_t *oss) { struct proc *p = td->td_proc; int oonstack; oonstack = sigonstack(cpu_getstack(td)); if (oss != NULL) { *oss = td->td_sigstk; oss->ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; } if (ss != NULL) { if (oonstack) return (EPERM); if ((ss->ss_flags & ~SS_DISABLE) != 0) return (EINVAL); if (!(ss->ss_flags & SS_DISABLE)) { if (ss->ss_size < p->p_sysent->sv_minsigstksz) return (ENOMEM); td->td_sigstk = *ss; td->td_pflags |= TDP_ALTSTACK; } else { td->td_pflags &= ~TDP_ALTSTACK; } } return (0); } /* * Common code for kill process group/broadcast kill. * cp is calling process. */ static int killpg1(struct thread *td, int sig, int pgid, int all, ksiginfo_t *ksi) { struct proc *p; struct pgrp *pgrp; int err; int ret; ret = ESRCH; if (all) { /* * broadcast */ sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || p == td->td_proc || p->p_state == PRS_NEW) { continue; } PROC_LOCK(p); err = p_cansignal(td, p, sig); if (err == 0) { if (sig) pksignal(p, sig, ksi); ret = err; } else if (ret == ESRCH) ret = err; PROC_UNLOCK(p); } sx_sunlock(&allproc_lock); } else { sx_slock(&proctree_lock); if (pgid == 0) { /* * zero pgid means send to my process group. */ pgrp = td->td_proc->p_pgrp; PGRP_LOCK(pgrp); } else { pgrp = pgfind(pgid); if (pgrp == NULL) { sx_sunlock(&proctree_lock); return (ESRCH); } } sx_sunlock(&proctree_lock); LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || p->p_state == PRS_NEW) { PROC_UNLOCK(p); continue; } err = p_cansignal(td, p, sig); if (err == 0) { if (sig) pksignal(p, sig, ksi); ret = err; } else if (ret == ESRCH) ret = err; PROC_UNLOCK(p); } PGRP_UNLOCK(pgrp); } return (ret); } #ifndef _SYS_SYSPROTO_H_ struct kill_args { int pid; int signum; }; #endif /* ARGSUSED */ int sys_kill(struct thread *td, struct kill_args *uap) { ksiginfo_t ksi; struct proc *p; int error; /* * A process in capability mode can send signals only to himself. * The main rationale behind this is that abort(3) is implemented as * kill(getpid(), SIGABRT). */ if (IN_CAPABILITY_MODE(td) && uap->pid != td->td_proc->p_pid) return (ECAPMODE); AUDIT_ARG_SIGNUM(uap->signum); AUDIT_ARG_PID(uap->pid); if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); ksiginfo_init(&ksi); ksi.ksi_signo = uap->signum; ksi.ksi_code = SI_USER; ksi.ksi_pid = td->td_proc->p_pid; ksi.ksi_uid = td->td_ucred->cr_ruid; if (uap->pid > 0) { /* kill single process */ if ((p = pfind_any(uap->pid)) == NULL) return (ESRCH); AUDIT_ARG_PROCESS(p); error = p_cansignal(td, p, uap->signum); if (error == 0 && uap->signum) pksignal(p, uap->signum, &ksi); PROC_UNLOCK(p); return (error); } switch (uap->pid) { case -1: /* broadcast signal */ return (killpg1(td, uap->signum, 0, 1, &ksi)); case 0: /* signal own process group */ return (killpg1(td, uap->signum, 0, 0, &ksi)); default: /* negative explicit process group */ return (killpg1(td, uap->signum, -uap->pid, 0, &ksi)); } /* NOTREACHED */ } int sys_pdkill(struct thread *td, struct pdkill_args *uap) { struct proc *p; int error; AUDIT_ARG_SIGNUM(uap->signum); AUDIT_ARG_FD(uap->fd); if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); error = procdesc_find(td, uap->fd, &cap_pdkill_rights, &p); if (error) return (error); AUDIT_ARG_PROCESS(p); error = p_cansignal(td, p, uap->signum); if (error == 0 && uap->signum) kern_psignal(p, uap->signum); PROC_UNLOCK(p); return (error); } #if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct okillpg_args { int pgid; int signum; }; #endif /* ARGSUSED */ int okillpg(struct thread *td, struct okillpg_args *uap) { ksiginfo_t ksi; AUDIT_ARG_SIGNUM(uap->signum); AUDIT_ARG_PID(uap->pgid); if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); ksiginfo_init(&ksi); ksi.ksi_signo = uap->signum; ksi.ksi_code = SI_USER; ksi.ksi_pid = td->td_proc->p_pid; ksi.ksi_uid = td->td_ucred->cr_ruid; return (killpg1(td, uap->signum, uap->pgid, 0, &ksi)); } #endif /* COMPAT_43 */ #ifndef _SYS_SYSPROTO_H_ struct sigqueue_args { pid_t pid; int signum; /* union sigval */ void *value; }; #endif int sys_sigqueue(struct thread *td, struct sigqueue_args *uap) { union sigval sv; sv.sival_ptr = uap->value; return (kern_sigqueue(td, uap->pid, uap->signum, &sv)); } int kern_sigqueue(struct thread *td, pid_t pid, int signum, union sigval *value) { ksiginfo_t ksi; struct proc *p; int error; if ((u_int)signum > _SIG_MAXSIG) return (EINVAL); /* * Specification says sigqueue can only send signal to * single process. */ if (pid <= 0) return (EINVAL); if ((p = pfind_any(pid)) == NULL) return (ESRCH); error = p_cansignal(td, p, signum); if (error == 0 && signum != 0) { ksiginfo_init(&ksi); ksi.ksi_flags = KSI_SIGQ; ksi.ksi_signo = signum; ksi.ksi_code = SI_QUEUE; ksi.ksi_pid = td->td_proc->p_pid; ksi.ksi_uid = td->td_ucred->cr_ruid; ksi.ksi_value = *value; error = pksignal(p, ksi.ksi_signo, &ksi); } PROC_UNLOCK(p); return (error); } /* * Send a signal to a process group. */ void gsignal(int pgid, int sig, ksiginfo_t *ksi) { struct pgrp *pgrp; if (pgid != 0) { sx_slock(&proctree_lock); pgrp = pgfind(pgid); sx_sunlock(&proctree_lock); if (pgrp != NULL) { pgsignal(pgrp, sig, 0, ksi); PGRP_UNLOCK(pgrp); } } } /* * Send a signal to a process group. If checktty is 1, * limit to members which have a controlling terminal. */ void pgsignal(struct pgrp *pgrp, int sig, int checkctty, ksiginfo_t *ksi) { struct proc *p; if (pgrp) { PGRP_LOCK_ASSERT(pgrp, MA_OWNED); LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (p->p_state == PRS_NORMAL && (checkctty == 0 || p->p_flag & P_CONTROLT)) pksignal(p, sig, ksi); PROC_UNLOCK(p); } } } /* * Recalculate the signal mask and reset the signal disposition after * usermode frame for delivery is formed. Should be called after * mach-specific routine, because sysent->sv_sendsig() needs correct * ps_siginfo and signal mask. */ static void postsig_done(int sig, struct thread *td, struct sigacts *ps) { sigset_t mask; mtx_assert(&ps->ps_mtx, MA_OWNED); td->td_ru.ru_nsignals++; mask = ps->ps_catchmask[_SIG_IDX(sig)]; if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(mask, sig); kern_sigprocmask(td, SIG_BLOCK, &mask, NULL, SIGPROCMASK_PROC_LOCKED | SIGPROCMASK_PS_LOCKED); if (SIGISMEMBER(ps->ps_sigreset, sig)) sigdflt(ps, sig); } /* * Send a signal caused by a trap to the current thread. If it will be * caught immediately, deliver it with correct code. Otherwise, post it * normally. */ void trapsignal(struct thread *td, ksiginfo_t *ksi) { struct sigacts *ps; struct proc *p; int sig; int code; p = td->td_proc; sig = ksi->ksi_signo; code = ksi->ksi_code; KASSERT(_SIG_VALID(sig), ("invalid signal")); PROC_LOCK(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); if ((p->p_flag & P_TRACED) == 0 && SIGISMEMBER(ps->ps_sigcatch, sig) && !SIGISMEMBER(td->td_sigmask, sig)) { #ifdef KTRACE if (KTRPOINT(curthread, KTR_PSIG)) ktrpsig(sig, ps->ps_sigact[_SIG_IDX(sig)], &td->td_sigmask, code); #endif (*p->p_sysent->sv_sendsig)(ps->ps_sigact[_SIG_IDX(sig)], ksi, &td->td_sigmask); postsig_done(sig, td, ps); mtx_unlock(&ps->ps_mtx); } else { /* * Avoid a possible infinite loop if the thread * masking the signal or process is ignoring the * signal. */ if (kern_forcesigexit && (SIGISMEMBER(td->td_sigmask, sig) || ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN)) { SIGDELSET(td->td_sigmask, sig); SIGDELSET(ps->ps_sigcatch, sig); SIGDELSET(ps->ps_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } mtx_unlock(&ps->ps_mtx); p->p_code = code; /* XXX for core dump/debugger */ p->p_sig = sig; /* XXX to verify code */ tdsendsignal(p, td, sig, ksi); } PROC_UNLOCK(p); } static struct thread * sigtd(struct proc *p, int sig, int prop) { struct thread *td, *signal_td; PROC_LOCK_ASSERT(p, MA_OWNED); /* * Check if current thread can handle the signal without * switching context to another thread. */ if (curproc == p && !SIGISMEMBER(curthread->td_sigmask, sig)) return (curthread); signal_td = NULL; FOREACH_THREAD_IN_PROC(p, td) { if (!SIGISMEMBER(td->td_sigmask, sig)) { signal_td = td; break; } } if (signal_td == NULL) signal_td = FIRST_THREAD_IN_PROC(p); return (signal_td); } /* * Send the signal to the process. If the signal has an action, the action * is usually performed by the target process rather than the caller; we add * the signal to the set of pending signals for the process. * * Exceptions: * o When a stop signal is sent to a sleeping process that takes the * default action, the process is stopped without awakening it. * o SIGCONT restarts stopped processes (or puts them back to sleep) * regardless of the signal action (eg, blocked or ignored). * * Other ignored signals are discarded immediately. * * NB: This function may be entered from the debugger via the "kill" DDB * command. There is little that can be done to mitigate the possibly messy * side effects of this unwise possibility. */ void kern_psignal(struct proc *p, int sig) { ksiginfo_t ksi; ksiginfo_init(&ksi); ksi.ksi_signo = sig; ksi.ksi_code = SI_KERNEL; (void) tdsendsignal(p, NULL, sig, &ksi); } int pksignal(struct proc *p, int sig, ksiginfo_t *ksi) { return (tdsendsignal(p, NULL, sig, ksi)); } /* Utility function for finding a thread to send signal event to. */ int sigev_findtd(struct proc *p ,struct sigevent *sigev, struct thread **ttd) { struct thread *td; if (sigev->sigev_notify == SIGEV_THREAD_ID) { td = tdfind(sigev->sigev_notify_thread_id, p->p_pid); if (td == NULL) return (ESRCH); *ttd = td; } else { *ttd = NULL; PROC_LOCK(p); } return (0); } void tdsignal(struct thread *td, int sig) { ksiginfo_t ksi; ksiginfo_init(&ksi); ksi.ksi_signo = sig; ksi.ksi_code = SI_KERNEL; (void) tdsendsignal(td->td_proc, td, sig, &ksi); } void tdksignal(struct thread *td, int sig, ksiginfo_t *ksi) { (void) tdsendsignal(td->td_proc, td, sig, ksi); } int tdsendsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi) { sig_t action; sigqueue_t *sigqueue; int prop; struct sigacts *ps; int intrval; int ret = 0; int wakeup_swapper; MPASS(td == NULL || p == td->td_proc); PROC_LOCK_ASSERT(p, MA_OWNED); if (!_SIG_VALID(sig)) panic("%s(): invalid signal %d", __func__, sig); KASSERT(ksi == NULL || !KSI_ONQ(ksi), ("%s: ksi on queue", __func__)); /* * IEEE Std 1003.1-2001: return success when killing a zombie. */ if (p->p_state == PRS_ZOMBIE) { if (ksi && (ksi->ksi_flags & KSI_INS)) ksiginfo_tryfree(ksi); return (ret); } ps = p->p_sigacts; KNOTE_LOCKED(p->p_klist, NOTE_SIGNAL | sig); prop = sigprop(sig); if (td == NULL) { td = sigtd(p, sig, prop); sigqueue = &p->p_sigqueue; } else sigqueue = &td->td_sigqueue; SDT_PROBE3(proc, , , signal__send, td, p, sig); /* * If the signal is being ignored, * then we forget about it immediately. * (Note: we don't set SIGCONT in ps_sigignore, * and if it is set to SIG_IGN, * action will be SIG_DFL here.) */ mtx_lock(&ps->ps_mtx); if (SIGISMEMBER(ps->ps_sigignore, sig)) { SDT_PROBE3(proc, , , signal__discard, td, p, sig); mtx_unlock(&ps->ps_mtx); if (ksi && (ksi->ksi_flags & KSI_INS)) ksiginfo_tryfree(ksi); return (ret); } if (SIGISMEMBER(td->td_sigmask, sig)) action = SIG_HOLD; else if (SIGISMEMBER(ps->ps_sigcatch, sig)) action = SIG_CATCH; else action = SIG_DFL; if (SIGISMEMBER(ps->ps_sigintr, sig)) intrval = EINTR; else intrval = ERESTART; mtx_unlock(&ps->ps_mtx); if (prop & SIGPROP_CONT) sigqueue_delete_stopmask_proc(p); else if (prop & SIGPROP_STOP) { /* * If sending a tty stop signal to a member of an orphaned * process group, discard the signal here if the action * is default; don't stop the process below if sleeping, * and don't clear any pending SIGCONT. */ if ((prop & SIGPROP_TTYSTOP) && (p->p_pgrp->pg_jobc == 0) && (action == SIG_DFL)) { if (ksi && (ksi->ksi_flags & KSI_INS)) ksiginfo_tryfree(ksi); return (ret); } sigqueue_delete_proc(p, SIGCONT); if (p->p_flag & P_CONTINUED) { p->p_flag &= ~P_CONTINUED; PROC_LOCK(p->p_pptr); sigqueue_take(p->p_ksi); PROC_UNLOCK(p->p_pptr); } } ret = sigqueue_add(sigqueue, sig, ksi); if (ret != 0) return (ret); signotify(td); /* * Defer further processing for signals which are held, * except that stopped processes must be continued by SIGCONT. */ if (action == SIG_HOLD && !((prop & SIGPROP_CONT) && (p->p_flag & P_STOPPED_SIG))) return (ret); /* SIGKILL: Remove procfs STOPEVENTs. */ if (sig == SIGKILL) { /* from procfs_ioctl.c: PIOCBIC */ p->p_stops = 0; /* from procfs_ioctl.c: PIOCCONT */ p->p_step = 0; wakeup(&p->p_step); } /* * Some signals have a process-wide effect and a per-thread * component. Most processing occurs when the process next * tries to cross the user boundary, however there are some * times when processing needs to be done immediately, such as * waking up threads so that they can cross the user boundary. * We try to do the per-process part here. */ if (P_SHOULDSTOP(p)) { KASSERT(!(p->p_flag & P_WEXIT), ("signal to stopped but exiting process")); if (sig == SIGKILL) { /* * If traced process is already stopped, * then no further action is necessary. */ if (p->p_flag & P_TRACED) goto out; /* * SIGKILL sets process running. * It will die elsewhere. * All threads must be restarted. */ p->p_flag &= ~P_STOPPED_SIG; goto runfast; } if (prop & SIGPROP_CONT) { /* * If traced process is already stopped, * then no further action is necessary. */ if (p->p_flag & P_TRACED) goto out; /* * If SIGCONT is default (or ignored), we continue the * process but don't leave the signal in sigqueue as * it has no further action. If SIGCONT is held, we * continue the process and leave the signal in * sigqueue. If the process catches SIGCONT, let it * handle the signal itself. If it isn't waiting on * an event, it goes back to run state. * Otherwise, process goes back to sleep state. */ p->p_flag &= ~P_STOPPED_SIG; PROC_SLOCK(p); if (p->p_numthreads == p->p_suspcount) { PROC_SUNLOCK(p); p->p_flag |= P_CONTINUED; p->p_xsig = SIGCONT; PROC_LOCK(p->p_pptr); childproc_continued(p); PROC_UNLOCK(p->p_pptr); PROC_SLOCK(p); } if (action == SIG_DFL) { thread_unsuspend(p); PROC_SUNLOCK(p); sigqueue_delete(sigqueue, sig); goto out; } if (action == SIG_CATCH) { /* * The process wants to catch it so it needs * to run at least one thread, but which one? */ PROC_SUNLOCK(p); goto runfast; } /* * The signal is not ignored or caught. */ thread_unsuspend(p); PROC_SUNLOCK(p); goto out; } if (prop & SIGPROP_STOP) { /* * If traced process is already stopped, * then no further action is necessary. */ if (p->p_flag & P_TRACED) goto out; /* * Already stopped, don't need to stop again * (If we did the shell could get confused). * Just make sure the signal STOP bit set. */ p->p_flag |= P_STOPPED_SIG; sigqueue_delete(sigqueue, sig); goto out; } /* * All other kinds of signals: * If a thread is sleeping interruptibly, simulate a * wakeup so that when it is continued it will be made * runnable and can look at the signal. However, don't make * the PROCESS runnable, leave it stopped. * It may run a bit until it hits a thread_suspend_check(). */ wakeup_swapper = 0; PROC_SLOCK(p); thread_lock(td); if (TD_ON_SLEEPQ(td) && (td->td_flags & TDF_SINTR)) wakeup_swapper = sleepq_abort(td, intrval); thread_unlock(td); PROC_SUNLOCK(p); if (wakeup_swapper) kick_proc0(); goto out; /* * Mutexes are short lived. Threads waiting on them will * hit thread_suspend_check() soon. */ } else if (p->p_state == PRS_NORMAL) { if (p->p_flag & P_TRACED || action == SIG_CATCH) { tdsigwakeup(td, sig, action, intrval); goto out; } MPASS(action == SIG_DFL); if (prop & SIGPROP_STOP) { if (p->p_flag & (P_PPWAIT|P_WEXIT)) goto out; p->p_flag |= P_STOPPED_SIG; p->p_xsig = sig; PROC_SLOCK(p); wakeup_swapper = sig_suspend_threads(td, p, 1); if (p->p_numthreads == p->p_suspcount) { /* * only thread sending signal to another * process can reach here, if thread is sending * signal to its process, because thread does * not suspend itself here, p_numthreads * should never be equal to p_suspcount. */ thread_stopped(p); PROC_SUNLOCK(p); sigqueue_delete_proc(p, p->p_xsig); } else PROC_SUNLOCK(p); if (wakeup_swapper) kick_proc0(); goto out; } } else { /* Not in "NORMAL" state. discard the signal. */ sigqueue_delete(sigqueue, sig); goto out; } /* * The process is not stopped so we need to apply the signal to all the * running threads. */ runfast: tdsigwakeup(td, sig, action, intrval); PROC_SLOCK(p); thread_unsuspend(p); PROC_SUNLOCK(p); out: /* If we jump here, proc slock should not be owned. */ PROC_SLOCK_ASSERT(p, MA_NOTOWNED); return (ret); } /* * The force of a signal has been directed against a single * thread. We need to see what we can do about knocking it * out of any sleep it may be in etc. */ static void tdsigwakeup(struct thread *td, int sig, sig_t action, int intrval) { struct proc *p = td->td_proc; int prop; int wakeup_swapper; wakeup_swapper = 0; PROC_LOCK_ASSERT(p, MA_OWNED); prop = sigprop(sig); PROC_SLOCK(p); thread_lock(td); /* * Bring the priority of a thread up if we want it to get * killed in this lifetime. Be careful to avoid bumping the * priority of the idle thread, since we still allow to signal * kernel processes. */ if (action == SIG_DFL && (prop & SIGPROP_KILL) != 0 && td->td_priority > PUSER && !TD_IS_IDLETHREAD(td)) sched_prio(td, PUSER); if (TD_ON_SLEEPQ(td)) { /* * If thread is sleeping uninterruptibly * we can't interrupt the sleep... the signal will * be noticed when the process returns through * trap() or syscall(). */ if ((td->td_flags & TDF_SINTR) == 0) goto out; /* * If SIGCONT is default (or ignored) and process is * asleep, we are finished; the process should not * be awakened. */ if ((prop & SIGPROP_CONT) && action == SIG_DFL) { thread_unlock(td); PROC_SUNLOCK(p); sigqueue_delete(&p->p_sigqueue, sig); /* * It may be on either list in this state. * Remove from both for now. */ sigqueue_delete(&td->td_sigqueue, sig); return; } /* * Don't awaken a sleeping thread for SIGSTOP if the * STOP signal is deferred. */ if ((prop & SIGPROP_STOP) != 0 && (td->td_flags & (TDF_SBDRY | TDF_SERESTART | TDF_SEINTR)) == TDF_SBDRY) goto out; /* * Give low priority threads a better chance to run. */ if (td->td_priority > PUSER && !TD_IS_IDLETHREAD(td)) sched_prio(td, PUSER); wakeup_swapper = sleepq_abort(td, intrval); } else { /* * Other states do nothing with the signal immediately, * other than kicking ourselves if we are running. * It will either never be noticed, or noticed very soon. */ #ifdef SMP if (TD_IS_RUNNING(td) && td != curthread) forward_signal(td); #endif } out: PROC_SUNLOCK(p); thread_unlock(td); if (wakeup_swapper) kick_proc0(); } static int sig_suspend_threads(struct thread *td, struct proc *p, int sending) { struct thread *td2; int wakeup_swapper; PROC_LOCK_ASSERT(p, MA_OWNED); PROC_SLOCK_ASSERT(p, MA_OWNED); MPASS(sending || td == curthread); wakeup_swapper = 0; FOREACH_THREAD_IN_PROC(p, td2) { thread_lock(td2); td2->td_flags |= TDF_ASTPENDING | TDF_NEEDSUSPCHK; if ((TD_IS_SLEEPING(td2) || TD_IS_SWAPPED(td2)) && (td2->td_flags & TDF_SINTR)) { if (td2->td_flags & TDF_SBDRY) { /* * Once a thread is asleep with * TDF_SBDRY and without TDF_SERESTART * or TDF_SEINTR set, it should never * become suspended due to this check. */ KASSERT(!TD_IS_SUSPENDED(td2), ("thread with deferred stops suspended")); if (TD_SBDRY_INTR(td2)) wakeup_swapper |= sleepq_abort(td2, TD_SBDRY_ERRNO(td2)); } else if (!TD_IS_SUSPENDED(td2)) { thread_suspend_one(td2); } } else if (!TD_IS_SUSPENDED(td2)) { if (sending || td != td2) td2->td_flags |= TDF_ASTPENDING; #ifdef SMP if (TD_IS_RUNNING(td2) && td2 != td) forward_signal(td2); #endif } thread_unlock(td2); } return (wakeup_swapper); } /* * Stop the process for an event deemed interesting to the debugger. If si is * non-NULL, this is a signal exchange; the new signal requested by the * debugger will be returned for handling. If si is NULL, this is some other * type of interesting event. The debugger may request a signal be delivered in * that case as well, however it will be deferred until it can be handled. */ int ptracestop(struct thread *td, int sig, ksiginfo_t *si) { struct proc *p = td->td_proc; struct thread *td2; ksiginfo_t ksi; int prop; PROC_LOCK_ASSERT(p, MA_OWNED); KASSERT(!(p->p_flag & P_WEXIT), ("Stopping exiting process")); WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &p->p_mtx.lock_object, "Stopping for traced signal"); td->td_xsig = sig; if (si == NULL || (si->ksi_flags & KSI_PTRACE) == 0) { td->td_dbgflags |= TDB_XSIG; CTR4(KTR_PTRACE, "ptracestop: tid %d (pid %d) flags %#x sig %d", td->td_tid, p->p_pid, td->td_dbgflags, sig); PROC_SLOCK(p); while ((p->p_flag & P_TRACED) && (td->td_dbgflags & TDB_XSIG)) { if (P_KILLED(p)) { /* * Ensure that, if we've been PT_KILLed, the * exit status reflects that. Another thread * may also be in ptracestop(), having just * received the SIGKILL, but this thread was * unsuspended first. */ td->td_dbgflags &= ~TDB_XSIG; td->td_xsig = SIGKILL; p->p_ptevents = 0; break; } if (p->p_flag & P_SINGLE_EXIT && !(td->td_dbgflags & TDB_EXIT)) { /* * Ignore ptrace stops except for thread exit * events when the process exits. */ td->td_dbgflags &= ~TDB_XSIG; PROC_SUNLOCK(p); return (0); } /* * Make wait(2) work. Ensure that right after the * attach, the thread which was decided to become the * leader of attach gets reported to the waiter. * Otherwise, just avoid overwriting another thread's * assignment to p_xthread. If another thread has * already set p_xthread, the current thread will get * a chance to report itself upon the next iteration. */ if ((td->td_dbgflags & TDB_FSTP) != 0 || ((p->p_flag2 & P2_PTRACE_FSTP) == 0 && p->p_xthread == NULL)) { p->p_xsig = sig; p->p_xthread = td; td->td_dbgflags &= ~TDB_FSTP; p->p_flag2 &= ~P2_PTRACE_FSTP; p->p_flag |= P_STOPPED_SIG | P_STOPPED_TRACE; sig_suspend_threads(td, p, 0); } if ((td->td_dbgflags & TDB_STOPATFORK) != 0) { td->td_dbgflags &= ~TDB_STOPATFORK; } stopme: thread_suspend_switch(td, p); if (p->p_xthread == td) p->p_xthread = NULL; if (!(p->p_flag & P_TRACED)) break; if (td->td_dbgflags & TDB_SUSPEND) { if (p->p_flag & P_SINGLE_EXIT) break; goto stopme; } } PROC_SUNLOCK(p); } if (si != NULL && sig == td->td_xsig) { /* Parent wants us to take the original signal unchanged. */ si->ksi_flags |= KSI_HEAD; if (sigqueue_add(&td->td_sigqueue, sig, si) != 0) si->ksi_signo = 0; } else if (td->td_xsig != 0) { /* * If parent wants us to take a new signal, then it will leave * it in td->td_xsig; otherwise we just look for signals again. */ ksiginfo_init(&ksi); ksi.ksi_signo = td->td_xsig; ksi.ksi_flags |= KSI_PTRACE; prop = sigprop(td->td_xsig); td2 = sigtd(p, td->td_xsig, prop); tdsendsignal(p, td2, td->td_xsig, &ksi); if (td != td2) return (0); } return (td->td_xsig); } static void reschedule_signals(struct proc *p, sigset_t block, int flags) { struct sigacts *ps; struct thread *td; int sig; PROC_LOCK_ASSERT(p, MA_OWNED); ps = p->p_sigacts; mtx_assert(&ps->ps_mtx, (flags & SIGPROCMASK_PS_LOCKED) != 0 ? MA_OWNED : MA_NOTOWNED); if (SIGISEMPTY(p->p_siglist)) return; SIGSETAND(block, p->p_siglist); while ((sig = sig_ffs(&block)) != 0) { SIGDELSET(block, sig); td = sigtd(p, sig, 0); signotify(td); if (!(flags & SIGPROCMASK_PS_LOCKED)) mtx_lock(&ps->ps_mtx); if (p->p_flag & P_TRACED || (SIGISMEMBER(ps->ps_sigcatch, sig) && !SIGISMEMBER(td->td_sigmask, sig))) tdsigwakeup(td, sig, SIG_CATCH, (SIGISMEMBER(ps->ps_sigintr, sig) ? EINTR : ERESTART)); if (!(flags & SIGPROCMASK_PS_LOCKED)) mtx_unlock(&ps->ps_mtx); } } void tdsigcleanup(struct thread *td) { struct proc *p; sigset_t unblocked; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); sigqueue_flush(&td->td_sigqueue); if (p->p_numthreads == 1) return; /* * Since we cannot handle signals, notify signal post code * about this by filling the sigmask. * * Also, if needed, wake up thread(s) that do not block the * same signals as the exiting thread, since the thread might * have been selected for delivery and woken up. */ SIGFILLSET(unblocked); SIGSETNAND(unblocked, td->td_sigmask); SIGFILLSET(td->td_sigmask); reschedule_signals(p, unblocked, 0); } static int sigdeferstop_curr_flags(int cflags) { MPASS((cflags & (TDF_SEINTR | TDF_SERESTART)) == 0 || (cflags & TDF_SBDRY) != 0); return (cflags & (TDF_SBDRY | TDF_SEINTR | TDF_SERESTART)); } /* * Defer the delivery of SIGSTOP for the current thread, according to * the requested mode. Returns previous flags, which must be restored * by sigallowstop(). * * TDF_SBDRY, TDF_SEINTR, and TDF_SERESTART flags are only set and * cleared by the current thread, which allow the lock-less read-only * accesses below. */ int sigdeferstop_impl(int mode) { struct thread *td; int cflags, nflags; td = curthread; cflags = sigdeferstop_curr_flags(td->td_flags); switch (mode) { case SIGDEFERSTOP_NOP: nflags = cflags; break; case SIGDEFERSTOP_OFF: nflags = 0; break; case SIGDEFERSTOP_SILENT: nflags = (cflags | TDF_SBDRY) & ~(TDF_SEINTR | TDF_SERESTART); break; case SIGDEFERSTOP_EINTR: nflags = (cflags | TDF_SBDRY | TDF_SEINTR) & ~TDF_SERESTART; break; case SIGDEFERSTOP_ERESTART: nflags = (cflags | TDF_SBDRY | TDF_SERESTART) & ~TDF_SEINTR; break; default: panic("sigdeferstop: invalid mode %x", mode); break; } if (cflags == nflags) return (SIGDEFERSTOP_VAL_NCHG); thread_lock(td); td->td_flags = (td->td_flags & ~cflags) | nflags; thread_unlock(td); return (cflags); } /* * Restores the STOP handling mode, typically permitting the delivery * of SIGSTOP for the current thread. This does not immediately * suspend if a stop was posted. Instead, the thread will suspend * either via ast() or a subsequent interruptible sleep. */ void sigallowstop_impl(int prev) { struct thread *td; int cflags; KASSERT(prev != SIGDEFERSTOP_VAL_NCHG, ("failed sigallowstop")); KASSERT((prev & ~(TDF_SBDRY | TDF_SEINTR | TDF_SERESTART)) == 0, ("sigallowstop: incorrect previous mode %x", prev)); td = curthread; cflags = sigdeferstop_curr_flags(td->td_flags); if (cflags != prev) { thread_lock(td); td->td_flags = (td->td_flags & ~cflags) | prev; thread_unlock(td); } } /* * If the current process has received a signal (should be caught or cause * termination, should interrupt current syscall), return the signal number. * Stop signals with default action are processed immediately, then cleared; * they aren't returned. This is checked after each entry to the system for * a syscall or trap (though this can usually be done without calling issignal * by checking the pending signal masks in cursig.) The normal call * sequence is * * while (sig = cursig(curthread)) * postsig(sig); */ static int issignal(struct thread *td) { struct proc *p; struct sigacts *ps; struct sigqueue *queue; sigset_t sigpending; ksiginfo_t ksi; int prop, sig, traced; p = td->td_proc; ps = p->p_sigacts; mtx_assert(&ps->ps_mtx, MA_OWNED); PROC_LOCK_ASSERT(p, MA_OWNED); for (;;) { traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG); sigpending = td->td_sigqueue.sq_signals; SIGSETOR(sigpending, p->p_sigqueue.sq_signals); SIGSETNAND(sigpending, td->td_sigmask); if ((p->p_flag & P_PPWAIT) != 0 || (td->td_flags & (TDF_SBDRY | TDF_SERESTART | TDF_SEINTR)) == TDF_SBDRY) SIG_STOPSIGMASK(sigpending); if (SIGISEMPTY(sigpending)) /* no signal to send */ return (0); if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED && (p->p_flag2 & P2_PTRACE_FSTP) != 0 && SIGISMEMBER(sigpending, SIGSTOP)) { /* * If debugger just attached, always consume * SIGSTOP from ptrace(PT_ATTACH) first, to * execute the debugger attach ritual in * order. */ sig = SIGSTOP; td->td_dbgflags |= TDB_FSTP; } else { sig = sig_ffs(&sigpending); } if (p->p_stops & S_SIG) { mtx_unlock(&ps->ps_mtx); stopevent(p, S_SIG, sig); mtx_lock(&ps->ps_mtx); } /* * We should see pending but ignored signals * only if P_TRACED was on when they were posted. */ if (SIGISMEMBER(ps->ps_sigignore, sig) && (traced == 0)) { sigqueue_delete(&td->td_sigqueue, sig); sigqueue_delete(&p->p_sigqueue, sig); continue; } if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED) { /* * If traced, always stop. * Remove old signal from queue before the stop. * XXX shrug off debugger, it causes siginfo to * be thrown away. */ queue = &td->td_sigqueue; ksiginfo_init(&ksi); if (sigqueue_get(queue, sig, &ksi) == 0) { queue = &p->p_sigqueue; sigqueue_get(queue, sig, &ksi); } td->td_si = ksi.ksi_info; mtx_unlock(&ps->ps_mtx); sig = ptracestop(td, sig, &ksi); mtx_lock(&ps->ps_mtx); + td->td_si.si_signo = 0; + /* * Keep looking if the debugger discarded or * replaced the signal. */ if (sig == 0) continue; /* * If the signal became masked, re-queue it. */ if (SIGISMEMBER(td->td_sigmask, sig)) { ksi.ksi_flags |= KSI_HEAD; sigqueue_add(&p->p_sigqueue, sig, &ksi); continue; } /* * If the traced bit got turned off, requeue * the signal and go back up to the top to * rescan signals. This ensures that p_sig* * and p_sigact are consistent. */ if ((p->p_flag & P_TRACED) == 0) { ksi.ksi_flags |= KSI_HEAD; sigqueue_add(queue, sig, &ksi); continue; } } prop = sigprop(sig); /* * Decide whether the signal should be returned. * Return the signal's number, or fall through * to clear it from the pending mask. */ switch ((intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) { case (intptr_t)SIG_DFL: /* * Don't take default actions on system processes. */ if (p->p_pid <= 1) { #ifdef DIAGNOSTIC /* * Are you sure you want to ignore SIGSEGV * in init? XXX */ printf("Process (pid %lu) got signal %d\n", (u_long)p->p_pid, sig); #endif break; /* == ignore */ } /* * If there is a pending stop signal to process with * default action, stop here, then clear the signal. * Traced or exiting processes should ignore stops. * Additionally, a member of an orphaned process group * should ignore tty stops. */ if (prop & SIGPROP_STOP) { if (p->p_flag & (P_TRACED | P_WEXIT | P_SINGLE_EXIT) || (p->p_pgrp->pg_jobc == 0 && prop & SIGPROP_TTYSTOP)) break; /* == ignore */ if (TD_SBDRY_INTR(td)) { KASSERT((td->td_flags & TDF_SBDRY) != 0, ("lost TDF_SBDRY")); return (-1); } mtx_unlock(&ps->ps_mtx); WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &p->p_mtx.lock_object, "Catching SIGSTOP"); sigqueue_delete(&td->td_sigqueue, sig); sigqueue_delete(&p->p_sigqueue, sig); p->p_flag |= P_STOPPED_SIG; p->p_xsig = sig; PROC_SLOCK(p); sig_suspend_threads(td, p, 0); thread_suspend_switch(td, p); PROC_SUNLOCK(p); mtx_lock(&ps->ps_mtx); goto next; } else if (prop & SIGPROP_IGNORE) { /* * Except for SIGCONT, shouldn't get here. * Default action is to ignore; drop it. */ break; /* == ignore */ } else return (sig); /*NOTREACHED*/ case (intptr_t)SIG_IGN: /* * Masking above should prevent us ever trying * to take action on an ignored signal other * than SIGCONT, unless process is traced. */ if ((prop & SIGPROP_CONT) == 0 && (p->p_flag & P_TRACED) == 0) printf("issignal\n"); break; /* == ignore */ default: /* * This signal has an action, let * postsig() process it. */ return (sig); } sigqueue_delete(&td->td_sigqueue, sig); /* take the signal! */ sigqueue_delete(&p->p_sigqueue, sig); next:; } /* NOTREACHED */ } void thread_stopped(struct proc *p) { int n; PROC_LOCK_ASSERT(p, MA_OWNED); PROC_SLOCK_ASSERT(p, MA_OWNED); n = p->p_suspcount; if (p == curproc) n++; if ((p->p_flag & P_STOPPED_SIG) && (n == p->p_numthreads)) { PROC_SUNLOCK(p); p->p_flag &= ~P_WAITED; PROC_LOCK(p->p_pptr); childproc_stopped(p, (p->p_flag & P_TRACED) ? CLD_TRAPPED : CLD_STOPPED); PROC_UNLOCK(p->p_pptr); PROC_SLOCK(p); } } /* * Take the action for the specified signal * from the current set of pending signals. */ int postsig(int sig) { struct thread *td; struct proc *p; struct sigacts *ps; sig_t action; ksiginfo_t ksi; sigset_t returnmask; KASSERT(sig != 0, ("postsig")); td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); ps = p->p_sigacts; mtx_assert(&ps->ps_mtx, MA_OWNED); ksiginfo_init(&ksi); if (sigqueue_get(&td->td_sigqueue, sig, &ksi) == 0 && sigqueue_get(&p->p_sigqueue, sig, &ksi) == 0) return (0); ksi.ksi_signo = sig; if (ksi.ksi_code == SI_TIMER) itimer_accept(p, ksi.ksi_timerid, &ksi); action = ps->ps_sigact[_SIG_IDX(sig)]; #ifdef KTRACE if (KTRPOINT(td, KTR_PSIG)) ktrpsig(sig, action, td->td_pflags & TDP_OLDMASK ? &td->td_oldsigmask : &td->td_sigmask, ksi.ksi_code); #endif if ((p->p_stops & S_SIG) != 0) { mtx_unlock(&ps->ps_mtx); stopevent(p, S_SIG, sig); mtx_lock(&ps->ps_mtx); } if (action == SIG_DFL) { /* * Default action, where the default is to kill * the process. (Other cases were ignored above.) */ mtx_unlock(&ps->ps_mtx); proc_td_siginfo_capture(td, &ksi.ksi_info); sigexit(td, sig); /* NOTREACHED */ } else { /* * If we get here, the signal must be caught. */ KASSERT(action != SIG_IGN, ("postsig action %p", action)); KASSERT(!SIGISMEMBER(td->td_sigmask, sig), ("postsig action: blocked sig %d", sig)); /* * Set the new mask value and also defer further * occurrences of this signal. * * Special case: user has done a sigsuspend. Here the * current mask is not of interest, but rather the * mask from before the sigsuspend is what we want * restored after the signal processing is completed. */ if (td->td_pflags & TDP_OLDMASK) { returnmask = td->td_oldsigmask; td->td_pflags &= ~TDP_OLDMASK; } else returnmask = td->td_sigmask; if (p->p_sig == sig) { p->p_code = 0; p->p_sig = 0; } (*p->p_sysent->sv_sendsig)(action, &ksi, &returnmask); postsig_done(sig, td, ps); } return (1); } void proc_wkilled(struct proc *p) { PROC_LOCK_ASSERT(p, MA_OWNED); if ((p->p_flag & P_WKILLED) == 0) { p->p_flag |= P_WKILLED; /* * Notify swapper that there is a process to swap in. * The notification is racy, at worst it would take 10 * seconds for the swapper process to notice. */ if ((p->p_flag & (P_INMEM | P_SWAPPINGIN)) == 0) wakeup(&proc0); } } /* * Kill the current process for stated reason. */ void killproc(struct proc *p, char *why) { PROC_LOCK_ASSERT(p, MA_OWNED); CTR3(KTR_PROC, "killproc: proc %p (pid %d, %s)", p, p->p_pid, p->p_comm); log(LOG_ERR, "pid %d (%s), uid %d, was killed: %s\n", p->p_pid, p->p_comm, p->p_ucred ? p->p_ucred->cr_uid : -1, why); proc_wkilled(p); kern_psignal(p, SIGKILL); } /* * Force the current process to exit with the specified signal, dumping core * if appropriate. We bypass the normal tests for masked and caught signals, * allowing unrecoverable failures to terminate the process without changing * signal state. Mark the accounting record with the signal termination. * If dumping core, save the signal number for the debugger. Calls exit and * does not return. */ void sigexit(struct thread *td, int sig) { struct proc *p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); p->p_acflag |= AXSIG; /* * We must be single-threading to generate a core dump. This * ensures that the registers in the core file are up-to-date. * Also, the ELF dump handler assumes that the thread list doesn't * change out from under it. * * XXX If another thread attempts to single-thread before us * (e.g. via fork()), we won't get a dump at all. */ if ((sigprop(sig) & SIGPROP_CORE) && thread_single(p, SINGLE_NO_EXIT) == 0) { p->p_sig = sig; /* * Log signals which would cause core dumps * (Log as LOG_INFO to appease those who don't want * these messages.) * XXX : Todo, as well as euid, write out ruid too * Note that coredump() drops proc lock. */ if (coredump(td) == 0) sig |= WCOREFLAG; if (kern_logsigexit) log(LOG_INFO, "pid %d (%s), uid %d: exited on signal %d%s\n", p->p_pid, p->p_comm, td->td_ucred ? td->td_ucred->cr_uid : -1, sig &~ WCOREFLAG, sig & WCOREFLAG ? " (core dumped)" : ""); } else PROC_UNLOCK(p); exit1(td, 0, sig); /* NOTREACHED */ } /* * Send queued SIGCHLD to parent when child process's state * is changed. */ static void sigparent(struct proc *p, int reason, int status) { PROC_LOCK_ASSERT(p, MA_OWNED); PROC_LOCK_ASSERT(p->p_pptr, MA_OWNED); if (p->p_ksi != NULL) { p->p_ksi->ksi_signo = SIGCHLD; p->p_ksi->ksi_code = reason; p->p_ksi->ksi_status = status; p->p_ksi->ksi_pid = p->p_pid; p->p_ksi->ksi_uid = p->p_ucred->cr_ruid; if (KSI_ONQ(p->p_ksi)) return; } pksignal(p->p_pptr, SIGCHLD, p->p_ksi); } static void childproc_jobstate(struct proc *p, int reason, int sig) { struct sigacts *ps; PROC_LOCK_ASSERT(p, MA_OWNED); PROC_LOCK_ASSERT(p->p_pptr, MA_OWNED); /* * Wake up parent sleeping in kern_wait(), also send * SIGCHLD to parent, but SIGCHLD does not guarantee * that parent will awake, because parent may masked * the signal. */ p->p_pptr->p_flag |= P_STATCHILD; wakeup(p->p_pptr); ps = p->p_pptr->p_sigacts; mtx_lock(&ps->ps_mtx); if ((ps->ps_flag & PS_NOCLDSTOP) == 0) { mtx_unlock(&ps->ps_mtx); sigparent(p, reason, sig); } else mtx_unlock(&ps->ps_mtx); } void childproc_stopped(struct proc *p, int reason) { childproc_jobstate(p, reason, p->p_xsig); } void childproc_continued(struct proc *p) { childproc_jobstate(p, CLD_CONTINUED, SIGCONT); } void childproc_exited(struct proc *p) { int reason, status; if (WCOREDUMP(p->p_xsig)) { reason = CLD_DUMPED; status = WTERMSIG(p->p_xsig); } else if (WIFSIGNALED(p->p_xsig)) { reason = CLD_KILLED; status = WTERMSIG(p->p_xsig); } else { reason = CLD_EXITED; status = p->p_xexit; } /* * XXX avoid calling wakeup(p->p_pptr), the work is * done in exit1(). */ sigparent(p, reason, status); } #define MAX_NUM_CORE_FILES 100000 #ifndef NUM_CORE_FILES #define NUM_CORE_FILES 5 #endif CTASSERT(NUM_CORE_FILES >= 0 && NUM_CORE_FILES <= MAX_NUM_CORE_FILES); static int num_cores = NUM_CORE_FILES; static int sysctl_debug_num_cores_check (SYSCTL_HANDLER_ARGS) { int error; int new_val; new_val = num_cores; error = sysctl_handle_int(oidp, &new_val, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (new_val > MAX_NUM_CORE_FILES) new_val = MAX_NUM_CORE_FILES; if (new_val < 0) new_val = 0; num_cores = new_val; return (0); } SYSCTL_PROC(_debug, OID_AUTO, ncores, CTLTYPE_INT|CTLFLAG_RW, 0, sizeof(int), sysctl_debug_num_cores_check, "I", "Maximum number of generated process corefiles while using index format"); #define GZIP_SUFFIX ".gz" #define ZSTD_SUFFIX ".zst" int compress_user_cores = 0; static int sysctl_compress_user_cores(SYSCTL_HANDLER_ARGS) { int error, val; val = compress_user_cores; error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (val != 0 && !compressor_avail(val)) return (EINVAL); compress_user_cores = val; return (error); } SYSCTL_PROC(_kern, OID_AUTO, compress_user_cores, CTLTYPE_INT | CTLFLAG_RWTUN, 0, sizeof(int), sysctl_compress_user_cores, "I", "Enable compression of user corefiles (" __XSTRING(COMPRESS_GZIP) " = gzip, " __XSTRING(COMPRESS_ZSTD) " = zstd)"); int compress_user_cores_level = 6; SYSCTL_INT(_kern, OID_AUTO, compress_user_cores_level, CTLFLAG_RWTUN, &compress_user_cores_level, 0, "Corefile compression level"); /* * Protect the access to corefilename[] by allproc_lock. */ #define corefilename_lock allproc_lock static char corefilename[MAXPATHLEN] = {"%N.core"}; TUNABLE_STR("kern.corefile", corefilename, sizeof(corefilename)); static int sysctl_kern_corefile(SYSCTL_HANDLER_ARGS) { int error; sx_xlock(&corefilename_lock); error = sysctl_handle_string(oidp, corefilename, sizeof(corefilename), req); sx_xunlock(&corefilename_lock); return (error); } SYSCTL_PROC(_kern, OID_AUTO, corefile, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0, sysctl_kern_corefile, "A", "Process corefile name format string"); static void vnode_close_locked(struct thread *td, struct vnode *vp) { VOP_UNLOCK(vp, 0); vn_close(vp, FWRITE, td->td_ucred, td); } /* * If the core format has a %I in it, then we need to check * for existing corefiles before defining a name. * To do this we iterate over 0..ncores to find a * non-existing core file name to use. If all core files are * already used we choose the oldest one. */ static int corefile_open_last(struct thread *td, char *name, int indexpos, int indexlen, int ncores, struct vnode **vpp) { struct vnode *oldvp, *nextvp, *vp; struct vattr vattr; struct nameidata nd; int error, i, flags, oflags, cmode; char ch; struct timespec lasttime; nextvp = oldvp = NULL; cmode = S_IRUSR | S_IWUSR; oflags = VN_OPEN_NOAUDIT | VN_OPEN_NAMECACHE | (capmode_coredump ? VN_OPEN_NOCAPCHECK : 0); for (i = 0; i < ncores; i++) { flags = O_CREAT | FWRITE | O_NOFOLLOW; ch = name[indexpos + indexlen]; (void)snprintf(name + indexpos, indexlen + 1, "%.*u", indexlen, i); name[indexpos + indexlen] = ch; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, td); error = vn_open_cred(&nd, &flags, cmode, oflags, td->td_ucred, NULL); if (error != 0) break; vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if ((flags & O_CREAT) == O_CREAT) { nextvp = vp; break; } error = VOP_GETATTR(vp, &vattr, td->td_ucred); if (error != 0) { vnode_close_locked(td, vp); break; } if (oldvp == NULL || lasttime.tv_sec > vattr.va_mtime.tv_sec || (lasttime.tv_sec == vattr.va_mtime.tv_sec && lasttime.tv_nsec >= vattr.va_mtime.tv_nsec)) { if (oldvp != NULL) vnode_close_locked(td, oldvp); oldvp = vp; lasttime = vattr.va_mtime; } else { vnode_close_locked(td, vp); } } if (oldvp != NULL) { if (nextvp == NULL) nextvp = oldvp; else vnode_close_locked(td, oldvp); } if (error != 0) { if (nextvp != NULL) vnode_close_locked(td, oldvp); } else { *vpp = nextvp; } return (error); } /* * corefile_open(comm, uid, pid, td, compress, vpp, namep) * Expand the name described in corefilename, using name, uid, and pid * and open/create core file. * corefilename is a printf-like string, with three format specifiers: * %N name of process ("name") * %P process id (pid) * %U user id (uid) * For example, "%N.core" is the default; they can be disabled completely * by using "/dev/null", or all core files can be stored in "/cores/%U/%N-%P". * This is controlled by the sysctl variable kern.corefile (see above). */ static int corefile_open(const char *comm, uid_t uid, pid_t pid, struct thread *td, int compress, struct vnode **vpp, char **namep) { struct sbuf sb; struct nameidata nd; const char *format; char *hostname, *name; int cmode, error, flags, i, indexpos, indexlen, oflags, ncores; hostname = NULL; format = corefilename; name = malloc(MAXPATHLEN, M_TEMP, M_WAITOK | M_ZERO); indexlen = 0; indexpos = -1; ncores = num_cores; (void)sbuf_new(&sb, name, MAXPATHLEN, SBUF_FIXEDLEN); sx_slock(&corefilename_lock); for (i = 0; format[i] != '\0'; i++) { switch (format[i]) { case '%': /* Format character */ i++; switch (format[i]) { case '%': sbuf_putc(&sb, '%'); break; case 'H': /* hostname */ if (hostname == NULL) { hostname = malloc(MAXHOSTNAMELEN, M_TEMP, M_WAITOK); } getcredhostname(td->td_ucred, hostname, MAXHOSTNAMELEN); sbuf_printf(&sb, "%s", hostname); break; case 'I': /* autoincrementing index */ if (indexpos != -1) { sbuf_printf(&sb, "%%I"); break; } indexpos = sbuf_len(&sb); sbuf_printf(&sb, "%u", ncores - 1); indexlen = sbuf_len(&sb) - indexpos; break; case 'N': /* process name */ sbuf_printf(&sb, "%s", comm); break; case 'P': /* process id */ sbuf_printf(&sb, "%u", pid); break; case 'U': /* user id */ sbuf_printf(&sb, "%u", uid); break; default: log(LOG_ERR, "Unknown format character %c in " "corename `%s'\n", format[i], format); break; } break; default: sbuf_putc(&sb, format[i]); break; } } sx_sunlock(&corefilename_lock); free(hostname, M_TEMP); if (compress == COMPRESS_GZIP) sbuf_printf(&sb, GZIP_SUFFIX); else if (compress == COMPRESS_ZSTD) sbuf_printf(&sb, ZSTD_SUFFIX); if (sbuf_error(&sb) != 0) { log(LOG_ERR, "pid %ld (%s), uid (%lu): corename is too " "long\n", (long)pid, comm, (u_long)uid); sbuf_delete(&sb); free(name, M_TEMP); return (ENOMEM); } sbuf_finish(&sb); sbuf_delete(&sb); if (indexpos != -1) { error = corefile_open_last(td, name, indexpos, indexlen, ncores, vpp); if (error != 0) { log(LOG_ERR, "pid %d (%s), uid (%u): Path `%s' failed " "on initial open test, error = %d\n", pid, comm, uid, name, error); } } else { cmode = S_IRUSR | S_IWUSR; oflags = VN_OPEN_NOAUDIT | VN_OPEN_NAMECACHE | (capmode_coredump ? VN_OPEN_NOCAPCHECK : 0); flags = O_CREAT | FWRITE | O_NOFOLLOW; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, td); error = vn_open_cred(&nd, &flags, cmode, oflags, td->td_ucred, NULL); if (error == 0) { *vpp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); } } if (error != 0) { #ifdef AUDIT audit_proc_coredump(td, name, error); #endif free(name, M_TEMP); return (error); } *namep = name; return (0); } /* * Dump a process' core. The main routine does some * policy checking, and creates the name of the coredump; * then it passes on a vnode and a size limit to the process-specific * coredump routine if there is one; if there _is not_ one, it returns * ENOSYS; otherwise it returns the error from the process-specific routine. */ static int coredump(struct thread *td) { struct proc *p = td->td_proc; struct ucred *cred = td->td_ucred; struct vnode *vp; struct flock lf; struct vattr vattr; int error, error1, locked; char *name; /* name of corefile */ void *rl_cookie; off_t limit; char *fullpath, *freepath = NULL; struct sbuf *sb; PROC_LOCK_ASSERT(p, MA_OWNED); MPASS((p->p_flag & P_HADTHREADS) == 0 || p->p_singlethread == td); _STOPEVENT(p, S_CORE, 0); if (!do_coredump || (!sugid_coredump && (p->p_flag & P_SUGID) != 0) || (p->p_flag2 & P2_NOTRACE) != 0) { PROC_UNLOCK(p); return (EFAULT); } /* * Note that the bulk of limit checking is done after * the corefile is created. The exception is if the limit * for corefiles is 0, in which case we don't bother * creating the corefile at all. This layout means that * a corefile is truncated instead of not being created, * if it is larger than the limit. */ limit = (off_t)lim_cur(td, RLIMIT_CORE); if (limit == 0 || racct_get_available(p, RACCT_CORE) == 0) { PROC_UNLOCK(p); return (EFBIG); } PROC_UNLOCK(p); error = corefile_open(p->p_comm, cred->cr_uid, p->p_pid, td, compress_user_cores, &vp, &name); if (error != 0) return (error); /* * Don't dump to non-regular files or files with links. * Do not dump into system files. */ if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred) != 0 || vattr.va_nlink != 1 || (vp->v_vflag & VV_SYSTEM) != 0) { VOP_UNLOCK(vp, 0); error = EFAULT; goto out; } VOP_UNLOCK(vp, 0); /* Postpone other writers, including core dumps of other processes. */ rl_cookie = vn_rangelock_wlock(vp, 0, OFF_MAX); lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_WRLCK; locked = (VOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &lf, F_FLOCK) == 0); VATTR_NULL(&vattr); vattr.va_size = 0; if (set_core_nodump_flag) vattr.va_flags = UF_NODUMP; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); VOP_SETATTR(vp, &vattr, cred); VOP_UNLOCK(vp, 0); PROC_LOCK(p); p->p_acflag |= ACORE; PROC_UNLOCK(p); if (p->p_sysent->sv_coredump != NULL) { error = p->p_sysent->sv_coredump(td, vp, limit, 0); } else { error = ENOSYS; } if (locked) { lf.l_type = F_UNLCK; VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &lf, F_FLOCK); } vn_rangelock_unlock(vp, rl_cookie); /* * Notify the userland helper that a process triggered a core dump. * This allows the helper to run an automated debugging session. */ if (error != 0 || coredump_devctl == 0) goto out; sb = sbuf_new_auto(); if (vn_fullpath_global(td, p->p_textvp, &fullpath, &freepath) != 0) goto out2; sbuf_printf(sb, "comm=\""); devctl_safe_quote_sb(sb, fullpath); free(freepath, M_TEMP); sbuf_printf(sb, "\" core=\""); /* * We can't lookup core file vp directly. When we're replacing a core, and * other random times, we flush the name cache, so it will fail. Instead, * if the path of the core is relative, add the current dir in front if it. */ if (name[0] != '/') { fullpath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); if (kern___getcwd(td, fullpath, UIO_SYSSPACE, MAXPATHLEN, MAXPATHLEN) != 0) { free(fullpath, M_TEMP); goto out2; } devctl_safe_quote_sb(sb, fullpath); free(fullpath, M_TEMP); sbuf_putc(sb, '/'); } devctl_safe_quote_sb(sb, name); sbuf_printf(sb, "\""); if (sbuf_finish(sb) == 0) devctl_notify("kernel", "signal", "coredump", sbuf_data(sb)); out2: sbuf_delete(sb); out: error1 = vn_close(vp, FWRITE, cred, td); if (error == 0) error = error1; #ifdef AUDIT audit_proc_coredump(td, name, error); #endif free(name, M_TEMP); return (error); } /* * Nonexistent system call-- signal process (may want to handle it). Flag * error in case process won't see signal immediately (blocked or ignored). */ #ifndef _SYS_SYSPROTO_H_ struct nosys_args { int dummy; }; #endif /* ARGSUSED */ int nosys(struct thread *td, struct nosys_args *args) { struct proc *p; p = td->td_proc; PROC_LOCK(p); tdsignal(td, SIGSYS); PROC_UNLOCK(p); if (kern_lognosys == 1 || kern_lognosys == 3) { uprintf("pid %d comm %s: nosys %d\n", p->p_pid, p->p_comm, td->td_sa.code); } if (kern_lognosys == 2 || kern_lognosys == 3) { printf("pid %d comm %s: nosys %d\n", p->p_pid, p->p_comm, td->td_sa.code); } return (ENOSYS); } /* * Send a SIGIO or SIGURG signal to a process or process group using stored * credentials rather than those of the current process. */ void pgsigio(struct sigio **sigiop, int sig, int checkctty) { ksiginfo_t ksi; struct sigio *sigio; ksiginfo_init(&ksi); ksi.ksi_signo = sig; ksi.ksi_code = SI_KERNEL; SIGIO_LOCK(); sigio = *sigiop; if (sigio == NULL) { SIGIO_UNLOCK(); return; } if (sigio->sio_pgid > 0) { PROC_LOCK(sigio->sio_proc); if (CANSIGIO(sigio->sio_ucred, sigio->sio_proc->p_ucred)) kern_psignal(sigio->sio_proc, sig); PROC_UNLOCK(sigio->sio_proc); } else if (sigio->sio_pgid < 0) { struct proc *p; PGRP_LOCK(sigio->sio_pgrp); LIST_FOREACH(p, &sigio->sio_pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (p->p_state == PRS_NORMAL && CANSIGIO(sigio->sio_ucred, p->p_ucred) && (checkctty == 0 || (p->p_flag & P_CONTROLT))) kern_psignal(p, sig); PROC_UNLOCK(p); } PGRP_UNLOCK(sigio->sio_pgrp); } SIGIO_UNLOCK(); } static int filt_sigattach(struct knote *kn) { struct proc *p = curproc; kn->kn_ptr.p_proc = p; kn->kn_flags |= EV_CLEAR; /* automatically set */ knlist_add(p->p_klist, kn, 0); return (0); } static void filt_sigdetach(struct knote *kn) { struct proc *p = kn->kn_ptr.p_proc; knlist_remove(p->p_klist, kn, 0); } /* * signal knotes are shared with proc knotes, so we apply a mask to * the hint in order to differentiate them from process hints. This * could be avoided by using a signal-specific knote list, but probably * isn't worth the trouble. */ static int filt_signal(struct knote *kn, long hint) { if (hint & NOTE_SIGNAL) { hint &= ~NOTE_SIGNAL; if (kn->kn_id == hint) kn->kn_data++; } return (kn->kn_data != 0); } struct sigacts * sigacts_alloc(void) { struct sigacts *ps; ps = malloc(sizeof(struct sigacts), M_SUBPROC, M_WAITOK | M_ZERO); refcount_init(&ps->ps_refcnt, 1); mtx_init(&ps->ps_mtx, "sigacts", NULL, MTX_DEF); return (ps); } void sigacts_free(struct sigacts *ps) { if (refcount_release(&ps->ps_refcnt) == 0) return; mtx_destroy(&ps->ps_mtx); free(ps, M_SUBPROC); } struct sigacts * sigacts_hold(struct sigacts *ps) { refcount_acquire(&ps->ps_refcnt); return (ps); } void sigacts_copy(struct sigacts *dest, struct sigacts *src) { KASSERT(dest->ps_refcnt == 1, ("sigacts_copy to shared dest")); mtx_lock(&src->ps_mtx); bcopy(src, dest, offsetof(struct sigacts, ps_refcnt)); mtx_unlock(&src->ps_mtx); } int sigacts_shared(struct sigacts *ps) { return (ps->ps_refcnt > 1); } Index: projects/clang700-import/sys/kern/kern_thread.c =================================================================== --- projects/clang700-import/sys/kern/kern_thread.c (revision 341812) +++ projects/clang700-import/sys/kern/kern_thread.c (revision 341813) @@ -1,1267 +1,1267 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2001 Julian Elischer . * 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(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 "opt_witness.h" #include "opt_hwpmc_hooks.h" #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 #ifdef HWPMC_HOOKS #include #endif #include #include #include #include #include /* * Asserts below verify the stability of struct thread and struct proc * layout, as exposed by KBI to modules. On head, the KBI is allowed * to drift, change to the structures must be accompanied by the * assert update. * * On the stable branches after KBI freeze, conditions must not be * violated. Typically new fields are moved to the end of the * structures. */ #ifdef __amd64__ _Static_assert(offsetof(struct thread, td_flags) == 0xfc, "struct thread KBI td_flags"); _Static_assert(offsetof(struct thread, td_pflags) == 0x104, "struct thread KBI td_pflags"); _Static_assert(offsetof(struct thread, td_frame) == 0x470, "struct thread KBI td_frame"); _Static_assert(offsetof(struct thread, td_emuldata) == 0x528, "struct thread KBI td_emuldata"); _Static_assert(offsetof(struct proc, p_flag) == 0xb0, "struct proc KBI p_flag"); _Static_assert(offsetof(struct proc, p_pid) == 0xbc, "struct proc KBI p_pid"); _Static_assert(offsetof(struct proc, p_filemon) == 0x3d0, "struct proc KBI p_filemon"); _Static_assert(offsetof(struct proc, p_comm) == 0x3e8, "struct proc KBI p_comm"); _Static_assert(offsetof(struct proc, p_emuldata) == 0x4c0, "struct proc KBI p_emuldata"); #endif #ifdef __i386__ _Static_assert(offsetof(struct thread, td_flags) == 0x98, "struct thread KBI td_flags"); _Static_assert(offsetof(struct thread, td_pflags) == 0xa0, "struct thread KBI td_pflags"); _Static_assert(offsetof(struct thread, td_frame) == 0x2e8, "struct thread KBI td_frame"); _Static_assert(offsetof(struct thread, td_emuldata) == 0x334, "struct thread KBI td_emuldata"); _Static_assert(offsetof(struct proc, p_flag) == 0x68, "struct proc KBI p_flag"); _Static_assert(offsetof(struct proc, p_pid) == 0x74, "struct proc KBI p_pid"); _Static_assert(offsetof(struct proc, p_filemon) == 0x27c, "struct proc KBI p_filemon"); _Static_assert(offsetof(struct proc, p_comm) == 0x290, "struct proc KBI p_comm"); _Static_assert(offsetof(struct proc, p_emuldata) == 0x31c, "struct proc KBI p_emuldata"); #endif SDT_PROVIDER_DECLARE(proc); SDT_PROBE_DEFINE(proc, , , lwp__exit); /* * thread related storage. */ static uma_zone_t thread_zone; TAILQ_HEAD(, thread) zombie_threads = TAILQ_HEAD_INITIALIZER(zombie_threads); static struct mtx zombie_lock; MTX_SYSINIT(zombie_lock, &zombie_lock, "zombie lock", MTX_SPIN); static void thread_zombie(struct thread *); static int thread_unsuspend_one(struct thread *td, struct proc *p, bool boundary); #define TID_BUFFER_SIZE 1024 struct mtx tid_lock; static struct unrhdr *tid_unrhdr; static lwpid_t tid_buffer[TID_BUFFER_SIZE]; static int tid_head, tid_tail; static MALLOC_DEFINE(M_TIDHASH, "tidhash", "thread hash"); struct tidhashhead *tidhashtbl; u_long tidhash; struct rwlock tidhash_lock; EVENTHANDLER_LIST_DEFINE(thread_ctor); EVENTHANDLER_LIST_DEFINE(thread_dtor); EVENTHANDLER_LIST_DEFINE(thread_init); EVENTHANDLER_LIST_DEFINE(thread_fini); static lwpid_t tid_alloc(void) { lwpid_t tid; tid = alloc_unr(tid_unrhdr); if (tid != -1) return (tid); mtx_lock(&tid_lock); if (tid_head == tid_tail) { mtx_unlock(&tid_lock); return (-1); } tid = tid_buffer[tid_head]; tid_head = (tid_head + 1) % TID_BUFFER_SIZE; mtx_unlock(&tid_lock); return (tid); } static void tid_free(lwpid_t tid) { lwpid_t tmp_tid = -1; mtx_lock(&tid_lock); if ((tid_tail + 1) % TID_BUFFER_SIZE == tid_head) { tmp_tid = tid_buffer[tid_head]; tid_head = (tid_head + 1) % TID_BUFFER_SIZE; } tid_buffer[tid_tail] = tid; tid_tail = (tid_tail + 1) % TID_BUFFER_SIZE; mtx_unlock(&tid_lock); if (tmp_tid != -1) free_unr(tid_unrhdr, tmp_tid); } /* * Prepare a thread for use. */ static int thread_ctor(void *mem, int size, void *arg, int flags) { struct thread *td; td = (struct thread *)mem; td->td_state = TDS_INACTIVE; - td->td_oncpu = NOCPU; + td->td_lastcpu = td->td_oncpu = NOCPU; td->td_tid = tid_alloc(); /* * Note that td_critnest begins life as 1 because the thread is not * running and is thereby implicitly waiting to be on the receiving * end of a context switch. */ td->td_critnest = 1; td->td_lend_user_pri = PRI_MAX; EVENTHANDLER_DIRECT_INVOKE(thread_ctor, td); #ifdef AUDIT audit_thread_alloc(td); #endif umtx_thread_alloc(td); return (0); } /* * Reclaim a thread after use. */ static void thread_dtor(void *mem, int size, void *arg) { struct thread *td; td = (struct thread *)mem; #ifdef INVARIANTS /* Verify that this thread is in a safe state to free. */ switch (td->td_state) { case TDS_INHIBITED: case TDS_RUNNING: case TDS_CAN_RUN: case TDS_RUNQ: /* * We must never unlink a thread that is in one of * these states, because it is currently active. */ panic("bad state for thread unlinking"); /* NOTREACHED */ case TDS_INACTIVE: break; default: panic("bad thread state"); /* NOTREACHED */ } #endif #ifdef AUDIT audit_thread_free(td); #endif /* Free all OSD associated to this thread. */ osd_thread_exit(td); td_softdep_cleanup(td); MPASS(td->td_su == NULL); EVENTHANDLER_DIRECT_INVOKE(thread_dtor, td); tid_free(td->td_tid); } /* * Initialize type-stable parts of a thread (when newly created). */ static int thread_init(void *mem, int size, int flags) { struct thread *td; td = (struct thread *)mem; td->td_sleepqueue = sleepq_alloc(); td->td_turnstile = turnstile_alloc(); td->td_rlqe = NULL; EVENTHANDLER_DIRECT_INVOKE(thread_init, td); umtx_thread_init(td); epoch_thread_init(td); td->td_kstack = 0; td->td_sel = NULL; return (0); } /* * Tear down type-stable parts of a thread (just before being discarded). */ static void thread_fini(void *mem, int size) { struct thread *td; td = (struct thread *)mem; EVENTHANDLER_DIRECT_INVOKE(thread_fini, td); rlqentry_free(td->td_rlqe); turnstile_free(td->td_turnstile); sleepq_free(td->td_sleepqueue); umtx_thread_fini(td); epoch_thread_fini(td); seltdfini(td); } /* * For a newly created process, * link up all the structures and its initial threads etc. * called from: * {arch}/{arch}/machdep.c {arch}_init(), init386() etc. * proc_dtor() (should go away) * proc_init() */ void proc_linkup0(struct proc *p, struct thread *td) { TAILQ_INIT(&p->p_threads); /* all threads in proc */ proc_linkup(p, td); } void proc_linkup(struct proc *p, struct thread *td) { sigqueue_init(&p->p_sigqueue, p); p->p_ksi = ksiginfo_alloc(1); if (p->p_ksi != NULL) { /* XXX p_ksi may be null if ksiginfo zone is not ready */ p->p_ksi->ksi_flags = KSI_EXT | KSI_INS; } LIST_INIT(&p->p_mqnotifier); p->p_numthreads = 0; thread_link(td, p); } /* * Initialize global thread allocation resources. */ void threadinit(void) { mtx_init(&tid_lock, "TID lock", NULL, MTX_DEF); /* * pid_max cannot be greater than PID_MAX. * leave one number for thread0. */ tid_unrhdr = new_unrhdr(PID_MAX + 2, INT_MAX, &tid_lock); thread_zone = uma_zcreate("THREAD", sched_sizeof_thread(), thread_ctor, thread_dtor, thread_init, thread_fini, 32 - 1, UMA_ZONE_NOFREE); tidhashtbl = hashinit(maxproc / 2, M_TIDHASH, &tidhash); rw_init(&tidhash_lock, "tidhash"); } /* * Place an unused thread on the zombie list. * Use the slpq as that must be unused by now. */ void thread_zombie(struct thread *td) { mtx_lock_spin(&zombie_lock); TAILQ_INSERT_HEAD(&zombie_threads, td, td_slpq); mtx_unlock_spin(&zombie_lock); } /* * Release a thread that has exited after cpu_throw(). */ void thread_stash(struct thread *td) { atomic_subtract_rel_int(&td->td_proc->p_exitthreads, 1); thread_zombie(td); } /* * Reap zombie resources. */ void thread_reap(void) { struct thread *td_first, *td_next; /* * Don't even bother to lock if none at this instant, * we really don't care about the next instant. */ if (!TAILQ_EMPTY(&zombie_threads)) { mtx_lock_spin(&zombie_lock); td_first = TAILQ_FIRST(&zombie_threads); if (td_first) TAILQ_INIT(&zombie_threads); mtx_unlock_spin(&zombie_lock); while (td_first) { td_next = TAILQ_NEXT(td_first, td_slpq); thread_cow_free(td_first); thread_free(td_first); td_first = td_next; } } } /* * Allocate a thread. */ struct thread * thread_alloc(int pages) { struct thread *td; thread_reap(); /* check if any zombies to get */ td = (struct thread *)uma_zalloc(thread_zone, M_WAITOK); KASSERT(td->td_kstack == 0, ("thread_alloc got thread with kstack")); if (!vm_thread_new(td, pages)) { uma_zfree(thread_zone, td); return (NULL); } cpu_thread_alloc(td); return (td); } int thread_alloc_stack(struct thread *td, int pages) { KASSERT(td->td_kstack == 0, ("thread_alloc_stack called on a thread with kstack")); if (!vm_thread_new(td, pages)) return (0); cpu_thread_alloc(td); return (1); } /* * Deallocate a thread. */ void thread_free(struct thread *td) { lock_profile_thread_exit(td); if (td->td_cpuset) cpuset_rel(td->td_cpuset); td->td_cpuset = NULL; cpu_thread_free(td); if (td->td_kstack != 0) vm_thread_dispose(td); callout_drain(&td->td_slpcallout); uma_zfree(thread_zone, td); } void thread_cow_get_proc(struct thread *newtd, struct proc *p) { PROC_LOCK_ASSERT(p, MA_OWNED); newtd->td_ucred = crhold(p->p_ucred); newtd->td_limit = lim_hold(p->p_limit); newtd->td_cowgen = p->p_cowgen; } void thread_cow_get(struct thread *newtd, struct thread *td) { newtd->td_ucred = crhold(td->td_ucred); newtd->td_limit = lim_hold(td->td_limit); newtd->td_cowgen = td->td_cowgen; } void thread_cow_free(struct thread *td) { if (td->td_ucred != NULL) crfree(td->td_ucred); if (td->td_limit != NULL) lim_free(td->td_limit); } void thread_cow_update(struct thread *td) { struct proc *p; struct ucred *oldcred; struct plimit *oldlimit; p = td->td_proc; oldcred = NULL; oldlimit = NULL; PROC_LOCK(p); if (td->td_ucred != p->p_ucred) { oldcred = td->td_ucred; td->td_ucred = crhold(p->p_ucred); } if (td->td_limit != p->p_limit) { oldlimit = td->td_limit; td->td_limit = lim_hold(p->p_limit); } td->td_cowgen = p->p_cowgen; PROC_UNLOCK(p); if (oldcred != NULL) crfree(oldcred); if (oldlimit != NULL) lim_free(oldlimit); } /* * Discard the current thread and exit from its context. * Always called with scheduler locked. * * Because we can't free a thread while we're operating under its context, * push the current thread into our CPU's deadthread holder. This means * we needn't worry about someone else grabbing our context before we * do a cpu_throw(). */ void thread_exit(void) { uint64_t runtime, new_switchtime; struct thread *td; struct thread *td2; struct proc *p; int wakeup_swapper; td = curthread; p = td->td_proc; PROC_SLOCK_ASSERT(p, MA_OWNED); mtx_assert(&Giant, MA_NOTOWNED); PROC_LOCK_ASSERT(p, MA_OWNED); KASSERT(p != NULL, ("thread exiting without a process")); CTR3(KTR_PROC, "thread_exit: thread %p (pid %ld, %s)", td, (long)p->p_pid, td->td_name); SDT_PROBE0(proc, , , lwp__exit); KASSERT(TAILQ_EMPTY(&td->td_sigqueue.sq_list), ("signal pending")); /* * drop FPU & debug register state storage, or any other * architecture specific resources that * would not be on a new untouched process. */ cpu_thread_exit(td); /* * The last thread is left attached to the process * So that the whole bundle gets recycled. Skip * all this stuff if we never had threads. * EXIT clears all sign of other threads when * it goes to single threading, so the last thread always * takes the short path. */ if (p->p_flag & P_HADTHREADS) { if (p->p_numthreads > 1) { atomic_add_int(&td->td_proc->p_exitthreads, 1); thread_unlink(td); td2 = FIRST_THREAD_IN_PROC(p); sched_exit_thread(td2, td); /* * The test below is NOT true if we are the * sole exiting thread. P_STOPPED_SINGLE is unset * in exit1() after it is the only survivor. */ if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) { if (p->p_numthreads == p->p_suspcount) { thread_lock(p->p_singlethread); wakeup_swapper = thread_unsuspend_one( p->p_singlethread, p, false); thread_unlock(p->p_singlethread); if (wakeup_swapper) kick_proc0(); } } PCPU_SET(deadthread, td); } else { /* * The last thread is exiting.. but not through exit() */ panic ("thread_exit: Last thread exiting on its own"); } } #ifdef HWPMC_HOOKS /* * If this thread is part of a process that is being tracked by hwpmc(4), * inform the module of the thread's impending exit. */ if (PMC_PROC_IS_USING_PMCS(td->td_proc)) { PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT); PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_EXIT, NULL); } else if (PMC_SYSTEM_SAMPLING_ACTIVE()) PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_EXIT_LOG, NULL); #endif PROC_UNLOCK(p); PROC_STATLOCK(p); thread_lock(td); PROC_SUNLOCK(p); /* Do the same timestamp bookkeeping that mi_switch() would do. */ new_switchtime = cpu_ticks(); runtime = new_switchtime - PCPU_GET(switchtime); td->td_runtime += runtime; td->td_incruntime += runtime; PCPU_SET(switchtime, new_switchtime); PCPU_SET(switchticks, ticks); VM_CNT_INC(v_swtch); /* Save our resource usage in our process. */ td->td_ru.ru_nvcsw++; ruxagg(p, td); rucollect(&p->p_ru, &td->td_ru); PROC_STATUNLOCK(p); td->td_state = TDS_INACTIVE; #ifdef WITNESS witness_thread_exit(td); #endif CTR1(KTR_PROC, "thread_exit: cpu_throw() thread %p", td); sched_throw(td); panic("I'm a teapot!"); /* NOTREACHED */ } /* * Do any thread specific cleanups that may be needed in wait() * called with Giant, proc and schedlock not held. */ void thread_wait(struct proc *p) { struct thread *td; mtx_assert(&Giant, MA_NOTOWNED); KASSERT(p->p_numthreads == 1, ("multiple threads in thread_wait()")); KASSERT(p->p_exitthreads == 0, ("p_exitthreads leaking")); td = FIRST_THREAD_IN_PROC(p); /* Lock the last thread so we spin until it exits cpu_throw(). */ thread_lock(td); thread_unlock(td); lock_profile_thread_exit(td); cpuset_rel(td->td_cpuset); td->td_cpuset = NULL; cpu_thread_clean(td); thread_cow_free(td); callout_drain(&td->td_slpcallout); thread_reap(); /* check for zombie threads etc. */ } /* * Link a thread to a process. * set up anything that needs to be initialized for it to * be used by the process. */ void thread_link(struct thread *td, struct proc *p) { /* * XXX This can't be enabled because it's called for proc0 before * its lock has been created. * PROC_LOCK_ASSERT(p, MA_OWNED); */ td->td_state = TDS_INACTIVE; td->td_proc = p; td->td_flags = TDF_INMEM; LIST_INIT(&td->td_contested); LIST_INIT(&td->td_lprof[0]); LIST_INIT(&td->td_lprof[1]); sigqueue_init(&td->td_sigqueue, p); callout_init(&td->td_slpcallout, 1); TAILQ_INSERT_TAIL(&p->p_threads, td, td_plist); p->p_numthreads++; } /* * Called from: * thread_exit() */ void thread_unlink(struct thread *td) { struct proc *p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); TAILQ_REMOVE(&p->p_threads, td, td_plist); p->p_numthreads--; /* could clear a few other things here */ /* Must NOT clear links to proc! */ } static int calc_remaining(struct proc *p, int mode) { int remaining; PROC_LOCK_ASSERT(p, MA_OWNED); PROC_SLOCK_ASSERT(p, MA_OWNED); if (mode == SINGLE_EXIT) remaining = p->p_numthreads; else if (mode == SINGLE_BOUNDARY) remaining = p->p_numthreads - p->p_boundary_count; else if (mode == SINGLE_NO_EXIT || mode == SINGLE_ALLPROC) remaining = p->p_numthreads - p->p_suspcount; else panic("calc_remaining: wrong mode %d", mode); return (remaining); } static int remain_for_mode(int mode) { return (mode == SINGLE_ALLPROC ? 0 : 1); } static int weed_inhib(int mode, struct thread *td2, struct proc *p) { int wakeup_swapper; PROC_LOCK_ASSERT(p, MA_OWNED); PROC_SLOCK_ASSERT(p, MA_OWNED); THREAD_LOCK_ASSERT(td2, MA_OWNED); wakeup_swapper = 0; switch (mode) { case SINGLE_EXIT: if (TD_IS_SUSPENDED(td2)) wakeup_swapper |= thread_unsuspend_one(td2, p, true); if (TD_ON_SLEEPQ(td2) && (td2->td_flags & TDF_SINTR) != 0) wakeup_swapper |= sleepq_abort(td2, EINTR); break; case SINGLE_BOUNDARY: case SINGLE_NO_EXIT: if (TD_IS_SUSPENDED(td2) && (td2->td_flags & TDF_BOUNDARY) == 0) wakeup_swapper |= thread_unsuspend_one(td2, p, false); if (TD_ON_SLEEPQ(td2) && (td2->td_flags & TDF_SINTR) != 0) wakeup_swapper |= sleepq_abort(td2, ERESTART); break; case SINGLE_ALLPROC: /* * ALLPROC suspend tries to avoid spurious EINTR for * threads sleeping interruptable, by suspending the * thread directly, similarly to sig_suspend_threads(). * Since such sleep is not performed at the user * boundary, TDF_BOUNDARY flag is not set, and TDF_ALLPROCSUSP * is used to avoid immediate un-suspend. */ if (TD_IS_SUSPENDED(td2) && (td2->td_flags & (TDF_BOUNDARY | TDF_ALLPROCSUSP)) == 0) wakeup_swapper |= thread_unsuspend_one(td2, p, false); if (TD_ON_SLEEPQ(td2) && (td2->td_flags & TDF_SINTR) != 0) { if ((td2->td_flags & TDF_SBDRY) == 0) { thread_suspend_one(td2); td2->td_flags |= TDF_ALLPROCSUSP; } else { wakeup_swapper |= sleepq_abort(td2, ERESTART); } } break; } return (wakeup_swapper); } /* * Enforce single-threading. * * Returns 1 if the caller must abort (another thread is waiting to * exit the process or similar). Process is locked! * Returns 0 when you are successfully the only thread running. * A process has successfully single threaded in the suspend mode when * There are no threads in user mode. Threads in the kernel must be * allowed to continue until they get to the user boundary. They may even * copy out their return values and data before suspending. They may however be * accelerated in reaching the user boundary as we will wake up * any sleeping threads that are interruptable. (PCATCH). */ int thread_single(struct proc *p, int mode) { struct thread *td; struct thread *td2; int remaining, wakeup_swapper; td = curthread; KASSERT(mode == SINGLE_EXIT || mode == SINGLE_BOUNDARY || mode == SINGLE_ALLPROC || mode == SINGLE_NO_EXIT, ("invalid mode %d", mode)); /* * If allowing non-ALLPROC singlethreading for non-curproc * callers, calc_remaining() and remain_for_mode() should be * adjusted to also account for td->td_proc != p. For now * this is not implemented because it is not used. */ KASSERT((mode == SINGLE_ALLPROC && td->td_proc != p) || (mode != SINGLE_ALLPROC && td->td_proc == p), ("mode %d proc %p curproc %p", mode, p, td->td_proc)); mtx_assert(&Giant, MA_NOTOWNED); PROC_LOCK_ASSERT(p, MA_OWNED); if ((p->p_flag & P_HADTHREADS) == 0 && mode != SINGLE_ALLPROC) return (0); /* Is someone already single threading? */ if (p->p_singlethread != NULL && p->p_singlethread != td) return (1); if (mode == SINGLE_EXIT) { p->p_flag |= P_SINGLE_EXIT; p->p_flag &= ~P_SINGLE_BOUNDARY; } else { p->p_flag &= ~P_SINGLE_EXIT; if (mode == SINGLE_BOUNDARY) p->p_flag |= P_SINGLE_BOUNDARY; else p->p_flag &= ~P_SINGLE_BOUNDARY; } if (mode == SINGLE_ALLPROC) p->p_flag |= P_TOTAL_STOP; p->p_flag |= P_STOPPED_SINGLE; PROC_SLOCK(p); p->p_singlethread = td; remaining = calc_remaining(p, mode); while (remaining != remain_for_mode(mode)) { if (P_SHOULDSTOP(p) != P_STOPPED_SINGLE) goto stopme; wakeup_swapper = 0; FOREACH_THREAD_IN_PROC(p, td2) { if (td2 == td) continue; thread_lock(td2); td2->td_flags |= TDF_ASTPENDING | TDF_NEEDSUSPCHK; if (TD_IS_INHIBITED(td2)) { wakeup_swapper |= weed_inhib(mode, td2, p); #ifdef SMP } else if (TD_IS_RUNNING(td2) && td != td2) { forward_signal(td2); #endif } thread_unlock(td2); } if (wakeup_swapper) kick_proc0(); remaining = calc_remaining(p, mode); /* * Maybe we suspended some threads.. was it enough? */ if (remaining == remain_for_mode(mode)) break; stopme: /* * Wake us up when everyone else has suspended. * In the mean time we suspend as well. */ thread_suspend_switch(td, p); remaining = calc_remaining(p, mode); } if (mode == SINGLE_EXIT) { /* * Convert the process to an unthreaded process. The * SINGLE_EXIT is called by exit1() or execve(), in * both cases other threads must be retired. */ KASSERT(p->p_numthreads == 1, ("Unthreading with >1 threads")); p->p_singlethread = NULL; p->p_flag &= ~(P_STOPPED_SINGLE | P_SINGLE_EXIT | P_HADTHREADS); /* * Wait for any remaining threads to exit cpu_throw(). */ while (p->p_exitthreads != 0) { PROC_SUNLOCK(p); PROC_UNLOCK(p); sched_relinquish(td); PROC_LOCK(p); PROC_SLOCK(p); } } else if (mode == SINGLE_BOUNDARY) { /* * Wait until all suspended threads are removed from * the processors. The thread_suspend_check() * increments p_boundary_count while it is still * running, which makes it possible for the execve() * to destroy vmspace while our other threads are * still using the address space. * * We lock the thread, which is only allowed to * succeed after context switch code finished using * the address space. */ FOREACH_THREAD_IN_PROC(p, td2) { if (td2 == td) continue; thread_lock(td2); KASSERT((td2->td_flags & TDF_BOUNDARY) != 0, ("td %p not on boundary", td2)); KASSERT(TD_IS_SUSPENDED(td2), ("td %p is not suspended", td2)); thread_unlock(td2); } } PROC_SUNLOCK(p); return (0); } bool thread_suspend_check_needed(void) { struct proc *p; struct thread *td; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); return (P_SHOULDSTOP(p) || ((p->p_flag & P_TRACED) != 0 && (td->td_dbgflags & TDB_SUSPEND) != 0)); } /* * Called in from locations that can safely check to see * whether we have to suspend or at least throttle for a * single-thread event (e.g. fork). * * Such locations include userret(). * If the "return_instead" argument is non zero, the thread must be able to * accept 0 (caller may continue), or 1 (caller must abort) as a result. * * The 'return_instead' argument tells the function if it may do a * thread_exit() or suspend, or whether the caller must abort and back * out instead. * * If the thread that set the single_threading request has set the * P_SINGLE_EXIT bit in the process flags then this call will never return * if 'return_instead' is false, but will exit. * * P_SINGLE_EXIT | return_instead == 0| return_instead != 0 *---------------+--------------------+--------------------- * 0 | returns 0 | returns 0 or 1 * | when ST ends | immediately *---------------+--------------------+--------------------- * 1 | thread exits | returns 1 * | | immediately * 0 = thread_exit() or suspension ok, * other = return error instead of stopping the thread. * * While a full suspension is under effect, even a single threading * thread would be suspended if it made this call (but it shouldn't). * This call should only be made from places where * thread_exit() would be safe as that may be the outcome unless * return_instead is set. */ int thread_suspend_check(int return_instead) { struct thread *td; struct proc *p; int wakeup_swapper; td = curthread; p = td->td_proc; mtx_assert(&Giant, MA_NOTOWNED); PROC_LOCK_ASSERT(p, MA_OWNED); while (thread_suspend_check_needed()) { if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) { KASSERT(p->p_singlethread != NULL, ("singlethread not set")); /* * The only suspension in action is a * single-threading. Single threader need not stop. * It is safe to access p->p_singlethread unlocked * because it can only be set to our address by us. */ if (p->p_singlethread == td) return (0); /* Exempt from stopping. */ } if ((p->p_flag & P_SINGLE_EXIT) && return_instead) return (EINTR); /* Should we goto user boundary if we didn't come from there? */ if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE && (p->p_flag & P_SINGLE_BOUNDARY) && return_instead) return (ERESTART); /* * Ignore suspend requests if they are deferred. */ if ((td->td_flags & TDF_SBDRY) != 0) { KASSERT(return_instead, ("TDF_SBDRY set for unsafe thread_suspend_check")); KASSERT((td->td_flags & (TDF_SEINTR | TDF_SERESTART)) != (TDF_SEINTR | TDF_SERESTART), ("both TDF_SEINTR and TDF_SERESTART")); return (TD_SBDRY_INTR(td) ? TD_SBDRY_ERRNO(td) : 0); } /* * If the process is waiting for us to exit, * this thread should just suicide. * Assumes that P_SINGLE_EXIT implies P_STOPPED_SINGLE. */ if ((p->p_flag & P_SINGLE_EXIT) && (p->p_singlethread != td)) { PROC_UNLOCK(p); /* * Allow Linux emulation layer to do some work * before thread suicide. */ if (__predict_false(p->p_sysent->sv_thread_detach != NULL)) (p->p_sysent->sv_thread_detach)(td); umtx_thread_exit(td); kern_thr_exit(td); panic("stopped thread did not exit"); } PROC_SLOCK(p); thread_stopped(p); if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) { if (p->p_numthreads == p->p_suspcount + 1) { thread_lock(p->p_singlethread); wakeup_swapper = thread_unsuspend_one( p->p_singlethread, p, false); thread_unlock(p->p_singlethread); if (wakeup_swapper) kick_proc0(); } } PROC_UNLOCK(p); thread_lock(td); /* * When a thread suspends, it just * gets taken off all queues. */ thread_suspend_one(td); if (return_instead == 0) { p->p_boundary_count++; td->td_flags |= TDF_BOUNDARY; } PROC_SUNLOCK(p); mi_switch(SW_INVOL | SWT_SUSPEND, NULL); thread_unlock(td); PROC_LOCK(p); } return (0); } void thread_suspend_switch(struct thread *td, struct proc *p) { KASSERT(!TD_IS_SUSPENDED(td), ("already suspended")); PROC_LOCK_ASSERT(p, MA_OWNED); PROC_SLOCK_ASSERT(p, MA_OWNED); /* * We implement thread_suspend_one in stages here to avoid * dropping the proc lock while the thread lock is owned. */ if (p == td->td_proc) { thread_stopped(p); p->p_suspcount++; } PROC_UNLOCK(p); thread_lock(td); td->td_flags &= ~TDF_NEEDSUSPCHK; TD_SET_SUSPENDED(td); sched_sleep(td, 0); PROC_SUNLOCK(p); DROP_GIANT(); mi_switch(SW_VOL | SWT_SUSPEND, NULL); thread_unlock(td); PICKUP_GIANT(); PROC_LOCK(p); PROC_SLOCK(p); } void thread_suspend_one(struct thread *td) { struct proc *p; p = td->td_proc; PROC_SLOCK_ASSERT(p, MA_OWNED); THREAD_LOCK_ASSERT(td, MA_OWNED); KASSERT(!TD_IS_SUSPENDED(td), ("already suspended")); p->p_suspcount++; td->td_flags &= ~TDF_NEEDSUSPCHK; TD_SET_SUSPENDED(td); sched_sleep(td, 0); } static int thread_unsuspend_one(struct thread *td, struct proc *p, bool boundary) { THREAD_LOCK_ASSERT(td, MA_OWNED); KASSERT(TD_IS_SUSPENDED(td), ("Thread not suspended")); TD_CLR_SUSPENDED(td); td->td_flags &= ~TDF_ALLPROCSUSP; if (td->td_proc == p) { PROC_SLOCK_ASSERT(p, MA_OWNED); p->p_suspcount--; if (boundary && (td->td_flags & TDF_BOUNDARY) != 0) { td->td_flags &= ~TDF_BOUNDARY; p->p_boundary_count--; } } return (setrunnable(td)); } /* * Allow all threads blocked by single threading to continue running. */ void thread_unsuspend(struct proc *p) { struct thread *td; int wakeup_swapper; PROC_LOCK_ASSERT(p, MA_OWNED); PROC_SLOCK_ASSERT(p, MA_OWNED); wakeup_swapper = 0; if (!P_SHOULDSTOP(p)) { FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); if (TD_IS_SUSPENDED(td)) { wakeup_swapper |= thread_unsuspend_one(td, p, true); } thread_unlock(td); } } else if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE && p->p_numthreads == p->p_suspcount) { /* * Stopping everything also did the job for the single * threading request. Now we've downgraded to single-threaded, * let it continue. */ if (p->p_singlethread->td_proc == p) { thread_lock(p->p_singlethread); wakeup_swapper = thread_unsuspend_one( p->p_singlethread, p, false); thread_unlock(p->p_singlethread); } } if (wakeup_swapper) kick_proc0(); } /* * End the single threading mode.. */ void thread_single_end(struct proc *p, int mode) { struct thread *td; int wakeup_swapper; KASSERT(mode == SINGLE_EXIT || mode == SINGLE_BOUNDARY || mode == SINGLE_ALLPROC || mode == SINGLE_NO_EXIT, ("invalid mode %d", mode)); PROC_LOCK_ASSERT(p, MA_OWNED); KASSERT((mode == SINGLE_ALLPROC && (p->p_flag & P_TOTAL_STOP) != 0) || (mode != SINGLE_ALLPROC && (p->p_flag & P_TOTAL_STOP) == 0), ("mode %d does not match P_TOTAL_STOP", mode)); KASSERT(mode == SINGLE_ALLPROC || p->p_singlethread == curthread, ("thread_single_end from other thread %p %p", curthread, p->p_singlethread)); KASSERT(mode != SINGLE_BOUNDARY || (p->p_flag & P_SINGLE_BOUNDARY) != 0, ("mis-matched SINGLE_BOUNDARY flags %x", p->p_flag)); p->p_flag &= ~(P_STOPPED_SINGLE | P_SINGLE_EXIT | P_SINGLE_BOUNDARY | P_TOTAL_STOP); PROC_SLOCK(p); p->p_singlethread = NULL; wakeup_swapper = 0; /* * If there are other threads they may now run, * unless of course there is a blanket 'stop order' * on the process. The single threader must be allowed * to continue however as this is a bad place to stop. */ if (p->p_numthreads != remain_for_mode(mode) && !P_SHOULDSTOP(p)) { FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); if (TD_IS_SUSPENDED(td)) { wakeup_swapper |= thread_unsuspend_one(td, p, mode == SINGLE_BOUNDARY); } thread_unlock(td); } } KASSERT(mode != SINGLE_BOUNDARY || p->p_boundary_count == 0, ("inconsistent boundary count %d", p->p_boundary_count)); PROC_SUNLOCK(p); if (wakeup_swapper) kick_proc0(); } struct thread * thread_find(struct proc *p, lwpid_t tid) { struct thread *td; PROC_LOCK_ASSERT(p, MA_OWNED); FOREACH_THREAD_IN_PROC(p, td) { if (td->td_tid == tid) break; } return (td); } /* Locate a thread by number; return with proc lock held. */ struct thread * tdfind(lwpid_t tid, pid_t pid) { #define RUN_THRESH 16 struct thread *td; int run = 0; rw_rlock(&tidhash_lock); LIST_FOREACH(td, TIDHASH(tid), td_hash) { if (td->td_tid == tid) { if (pid != -1 && td->td_proc->p_pid != pid) { td = NULL; break; } PROC_LOCK(td->td_proc); if (td->td_proc->p_state == PRS_NEW) { PROC_UNLOCK(td->td_proc); td = NULL; break; } if (run > RUN_THRESH) { if (rw_try_upgrade(&tidhash_lock)) { LIST_REMOVE(td, td_hash); LIST_INSERT_HEAD(TIDHASH(td->td_tid), td, td_hash); rw_wunlock(&tidhash_lock); return (td); } } break; } run++; } rw_runlock(&tidhash_lock); return (td); } void tidhash_add(struct thread *td) { rw_wlock(&tidhash_lock); LIST_INSERT_HEAD(TIDHASH(td->td_tid), td, td_hash); rw_wunlock(&tidhash_lock); } void tidhash_remove(struct thread *td) { rw_wlock(&tidhash_lock); LIST_REMOVE(td, td_hash); rw_wunlock(&tidhash_lock); } Index: projects/clang700-import/sys/kern/subr_blist.c =================================================================== --- projects/clang700-import/sys/kern/subr_blist.c (revision 341812) +++ projects/clang700-import/sys/kern/subr_blist.c (revision 341813) @@ -1,1146 +1,1159 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1998 Matthew Dillon. 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 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 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. */ /* * BLIST.C - Bitmap allocator/deallocator, using a radix tree with hinting * * This module implements a general bitmap allocator/deallocator. The * allocator eats around 2 bits per 'block'. The module does not * try to interpret the meaning of a 'block' other than to return * SWAPBLK_NONE on an allocation failure. * * A radix tree controls access to pieces of the bitmap, and includes * auxiliary information at each interior node about the availabilty of * contiguous free blocks in the subtree rooted at that node. Two radix * constants are involved: one for the size of the bitmaps contained in the * leaf nodes (BLIST_BMAP_RADIX), and one for the number of descendents of * each of the meta (interior) nodes (BLIST_META_RADIX). Each subtree is * associated with a range of blocks. The root of any subtree stores a * hint field that defines an upper bound on the size of the largest * allocation that can begin in the associated block range. A hint is an * upper bound on a potential allocation, but not necessarily a tight upper * bound. * * The bitmap field in each node directs the search for available blocks. * For a leaf node, a bit is set if the corresponding block is free. For a * meta node, a bit is set if the corresponding subtree contains a free * block somewhere within it. The search at a meta node considers only * children of that node that represent a range that includes a free block. * * The hinting greatly increases code efficiency for allocations while * the general radix structure optimizes both allocations and frees. The * radix tree should be able to operate well no matter how much * fragmentation there is and no matter how large a bitmap is used. * * The blist code wires all necessary memory at creation time. Neither * allocations nor frees require interaction with the memory subsystem. * The non-blocking nature of allocations and frees is required by swap * code (vm/swap_pager.c). * * LAYOUT: The radix tree is laid out recursively using a linear array. * Each meta node is immediately followed (laid out sequentially in * memory) by BLIST_META_RADIX lower level nodes. This is a recursive * structure but one that can be easily scanned through a very simple * 'skip' calculation. The memory allocation is only large enough to * cover the number of blocks requested at creation time. Nodes that * represent blocks beyond that limit, nodes that would never be read * or written, are not allocated, so that the last of the * BLIST_META_RADIX lower level nodes of a some nodes may not be * allocated. * * NOTE: the allocator cannot currently allocate more than * BLIST_BMAP_RADIX blocks per call. It will panic with 'allocation too * large' if you try. This is an area that could use improvement. The * radix is large enough that this restriction does not effect the swap * system, though. Currently only the allocation code is affected by * this algorithmic unfeature. The freeing code can handle arbitrary * ranges. * * This code can be compiled stand-alone for debugging. */ #include __FBSDID("$FreeBSD$"); #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #else #ifndef BLIST_NO_DEBUG #define BLIST_DEBUG #endif #include #include #include #include #include #include #include #include #include #include #define bitcount64(x) __bitcount64((uint64_t)(x)) #define malloc(a,b,c) calloc(a, 1) #define free(a,b) free(a) #define ummin(a,b) ((a) < (b) ? (a) : (b)) #include void panic(const char *ctl, ...); #endif /* * static support functions */ static daddr_t blst_leaf_alloc(blmeta_t *scan, daddr_t blk, int count); static daddr_t blst_meta_alloc(blmeta_t *scan, daddr_t cursor, daddr_t count, u_daddr_t radix); static void blst_leaf_free(blmeta_t *scan, daddr_t relblk, int count); static void blst_meta_free(blmeta_t *scan, daddr_t freeBlk, daddr_t count, u_daddr_t radix); static void blst_copy(blmeta_t *scan, daddr_t blk, daddr_t radix, blist_t dest, daddr_t count); static daddr_t blst_leaf_fill(blmeta_t *scan, daddr_t blk, int count); static daddr_t blst_meta_fill(blmeta_t *scan, daddr_t allocBlk, daddr_t count, u_daddr_t radix); #ifndef _KERNEL static void blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, int tab); #endif #ifdef _KERNEL static MALLOC_DEFINE(M_SWAP, "SWAP", "Swap space"); #endif _Static_assert(BLIST_BMAP_RADIX % BLIST_META_RADIX == 0, "radix divisibility error"); #define BLIST_BMAP_MASK (BLIST_BMAP_RADIX - 1) #define BLIST_META_MASK (BLIST_META_RADIX - 1) /* * For a subtree that can represent the state of up to 'radix' blocks, the * number of leaf nodes of the subtree is L=radix/BLIST_BMAP_RADIX. If 'm' * is short for BLIST_META_RADIX, then for a tree of height h with L=m**h * leaf nodes, the total number of tree nodes is 1 + m + m**2 + ... + m**h, * or, equivalently, (m**(h+1)-1)/(m-1). This quantity is called 'skip' * in the 'meta' functions that process subtrees. Since integer division * discards remainders, we can express this computation as * skip = (m * m**h) / (m - 1) * skip = (m * (radix / BLIST_BMAP_RADIX)) / (m - 1) * and since m divides BLIST_BMAP_RADIX, we can simplify further to * skip = (radix / (BLIST_BMAP_RADIX / m)) / (m - 1) * skip = radix / ((BLIST_BMAP_RADIX / m) * (m - 1)) * so that simple integer division by a constant can safely be used for the * calculation. */ static inline daddr_t radix_to_skip(daddr_t radix) { return (radix / ((BLIST_BMAP_RADIX / BLIST_META_RADIX) * BLIST_META_MASK)); } /* * Provide a mask with count bits set, starting as position n. */ static inline u_daddr_t bitrange(int n, int count) { return (((u_daddr_t)-1 << n) & ((u_daddr_t)-1 >> (BLIST_BMAP_RADIX - (n + count)))); } /* * Use binary search, or a faster method, to find the 1 bit in a u_daddr_t. * Assumes that the argument has only one bit set. */ static inline int bitpos(u_daddr_t mask) { int hi, lo, mid; switch (sizeof(mask)) { #ifdef HAVE_INLINE_FFSLL case sizeof(long long): return (ffsll(mask) - 1); #endif default: lo = 0; hi = BLIST_BMAP_RADIX; while (lo + 1 < hi) { mid = (lo + hi) >> 1; if ((mask >> mid) != 0) lo = mid; else hi = mid; } return (lo); } } /* * blist_create() - create a blist capable of handling up to the specified * number of blocks * * blocks - must be greater than 0 * flags - malloc flags * * The smallest blist consists of a single leaf node capable of * managing BLIST_BMAP_RADIX blocks. */ blist_t blist_create(daddr_t blocks, int flags) { blist_t bl; u_daddr_t nodes, radix; if (blocks == 0) panic("invalid block count"); /* * Calculate the radix and node count used for scanning. */ nodes = 1; radix = BLIST_BMAP_RADIX; while (radix <= blocks) { nodes += 1 + (blocks - 1) / radix; radix *= BLIST_META_RADIX; } bl = malloc(offsetof(struct blist, bl_root[nodes]), M_SWAP, flags | M_ZERO); if (bl == NULL) return (NULL); bl->bl_blocks = blocks; bl->bl_radix = radix; #if defined(BLIST_DEBUG) printf( "BLIST representing %lld blocks (%lld MB of swap)" ", requiring %lldK of ram\n", (long long)bl->bl_blocks, (long long)bl->bl_blocks * 4 / 1024, (long long)(nodes * sizeof(blmeta_t) + 1023) / 1024 ); printf("BLIST raw radix tree contains %lld records\n", (long long)nodes); #endif return (bl); } void blist_destroy(blist_t bl) { free(bl, M_SWAP); } /* * blist_alloc() - reserve space in the block bitmap. Return the base * of a contiguous region or SWAPBLK_NONE if space could * not be allocated. */ daddr_t blist_alloc(blist_t bl, daddr_t count) { daddr_t blk; if (count > BLIST_MAX_ALLOC) panic("allocation too large"); /* * This loop iterates at most twice. An allocation failure in the * first iteration leads to a second iteration only if the cursor was * non-zero. When the cursor is zero, an allocation failure will * stop further iterations. */ for (;;) { blk = blst_meta_alloc(bl->bl_root, bl->bl_cursor, count, bl->bl_radix); if (blk != SWAPBLK_NONE) { bl->bl_avail -= count; bl->bl_cursor = blk + count; if (bl->bl_cursor == bl->bl_blocks) bl->bl_cursor = 0; return (blk); } else if (bl->bl_cursor == 0) return (SWAPBLK_NONE); bl->bl_cursor = 0; } } /* * blist_avail() - return the number of free blocks. */ daddr_t blist_avail(blist_t bl) { return (bl->bl_avail); } /* * blist_free() - free up space in the block bitmap. Return the base * of a contiguous region. Panic if an inconsistancy is * found. */ void blist_free(blist_t bl, daddr_t blkno, daddr_t count) { if (blkno < 0 || blkno + count > bl->bl_blocks) panic("freeing invalid range"); blst_meta_free(bl->bl_root, blkno, count, bl->bl_radix); bl->bl_avail += count; } /* * blist_fill() - mark a region in the block bitmap as off-limits * to the allocator (i.e. allocate it), ignoring any * existing allocations. Return the number of blocks * actually filled that were free before the call. */ daddr_t blist_fill(blist_t bl, daddr_t blkno, daddr_t count) { daddr_t filled; if (blkno < 0 || blkno + count > bl->bl_blocks) panic("filling invalid range"); filled = blst_meta_fill(bl->bl_root, blkno, count, bl->bl_radix); bl->bl_avail -= filled; return (filled); } /* * blist_resize() - resize an existing radix tree to handle the * specified number of blocks. This will reallocate * the tree and transfer the previous bitmap to the new * one. When extending the tree you can specify whether * the new blocks are to left allocated or freed. */ void blist_resize(blist_t *pbl, daddr_t count, int freenew, int flags) { blist_t newbl = blist_create(count, flags); blist_t save = *pbl; *pbl = newbl; if (count > save->bl_blocks) count = save->bl_blocks; blst_copy(save->bl_root, 0, save->bl_radix, newbl, count); /* * If resizing upwards, should we free the new space or not? */ if (freenew && count < newbl->bl_blocks) { blist_free(newbl, count, newbl->bl_blocks - count); } blist_destroy(save); } #ifdef BLIST_DEBUG /* * blist_print() - dump radix tree */ void blist_print(blist_t bl) { printf("BLIST avail = %jd, cursor = %08jx {\n", (uintmax_t)bl->bl_avail, (uintmax_t)bl->bl_cursor); if (bl->bl_root->bm_bitmap != 0) blst_radix_print(bl->bl_root, 0, bl->bl_radix, 4); printf("}\n"); } #endif static const u_daddr_t fib[] = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, }; /* * Use 'gap' to describe a maximal range of unallocated blocks/bits. */ struct gap_stats { daddr_t start; /* current gap start, or SWAPBLK_NONE */ daddr_t num; /* number of gaps observed */ daddr_t max; /* largest gap size */ daddr_t avg; /* average gap size */ daddr_t err; /* sum - num * avg */ daddr_t histo[nitems(fib)]; /* # gaps in each size range */ int max_bucket; /* last histo elt with nonzero val */ }; /* * gap_stats_counting() - is the state 'counting 1 bits'? * or 'skipping 0 bits'? */ static inline bool gap_stats_counting(const struct gap_stats *stats) { return (stats->start != SWAPBLK_NONE); } /* * init_gap_stats() - initialize stats on gap sizes */ static inline void init_gap_stats(struct gap_stats *stats) { bzero(stats, sizeof(*stats)); stats->start = SWAPBLK_NONE; } /* * update_gap_stats() - update stats on gap sizes */ static void update_gap_stats(struct gap_stats *stats, daddr_t posn) { daddr_t size; int hi, lo, mid; if (!gap_stats_counting(stats)) { stats->start = posn; return; } size = posn - stats->start; stats->start = SWAPBLK_NONE; if (size > stats->max) stats->max = size; /* * Find the fibonacci range that contains size, * expecting to find it in an early range. */ lo = 0; hi = 1; while (hi < nitems(fib) && fib[hi] <= size) { lo = hi; hi *= 2; } if (hi >= nitems(fib)) hi = nitems(fib); while (lo + 1 != hi) { mid = (lo + hi) >> 1; if (fib[mid] <= size) lo = mid; else hi = mid; } stats->histo[lo]++; if (lo > stats->max_bucket) stats->max_bucket = lo; stats->err += size - stats->avg; stats->num++; stats->avg += stats->err / stats->num; stats->err %= stats->num; } /* * dump_gap_stats() - print stats on gap sizes */ static inline void dump_gap_stats(const struct gap_stats *stats, struct sbuf *s) { int i; sbuf_printf(s, "number of maximal free ranges: %jd\n", (intmax_t)stats->num); sbuf_printf(s, "largest free range: %jd\n", (intmax_t)stats->max); sbuf_printf(s, "average maximal free range size: %jd\n", (intmax_t)stats->avg); sbuf_printf(s, "number of maximal free ranges of different sizes:\n"); sbuf_printf(s, " count | size range\n"); sbuf_printf(s, " ----- | ----------\n"); for (i = 0; i < stats->max_bucket; i++) { if (stats->histo[i] != 0) { sbuf_printf(s, "%20jd | ", (intmax_t)stats->histo[i]); if (fib[i] != fib[i + 1] - 1) sbuf_printf(s, "%jd to %jd\n", (intmax_t)fib[i], (intmax_t)fib[i + 1] - 1); else sbuf_printf(s, "%jd\n", (intmax_t)fib[i]); } } sbuf_printf(s, "%20jd | ", (intmax_t)stats->histo[i]); if (stats->histo[i] > 1) sbuf_printf(s, "%jd to %jd\n", (intmax_t)fib[i], (intmax_t)stats->max); else sbuf_printf(s, "%jd\n", (intmax_t)stats->max); } /* * blist_stats() - dump radix tree stats */ void blist_stats(blist_t bl, struct sbuf *s) { struct gap_stats gstats; struct gap_stats *stats = &gstats; daddr_t i, nodes, radix; u_daddr_t bit, diff, mask; init_gap_stats(stats); nodes = 0; i = bl->bl_radix; while (i < bl->bl_radix + bl->bl_blocks) { /* * Find max size subtree starting at i. */ radix = BLIST_BMAP_RADIX; while (((i / radix) & BLIST_META_MASK) == 0) radix *= BLIST_META_RADIX; /* * Check for skippable subtrees starting at i. */ while (radix > BLIST_BMAP_RADIX) { if (bl->bl_root[nodes].bm_bitmap == 0) { if (gap_stats_counting(stats)) update_gap_stats(stats, i); break; } /* * Skip subtree root. */ nodes++; radix /= BLIST_META_RADIX; } if (radix == BLIST_BMAP_RADIX) { /* * Scan leaf. */ mask = bl->bl_root[nodes].bm_bitmap; diff = mask ^ (mask << 1); if (gap_stats_counting(stats)) diff ^= 1; while (diff != 0) { bit = diff & -diff; update_gap_stats(stats, i + bitpos(bit)); diff ^= bit; } } nodes += radix_to_skip(radix); i += radix; } update_gap_stats(stats, i); dump_gap_stats(stats, s); } /************************************************************************ * ALLOCATION SUPPORT FUNCTIONS * ************************************************************************ * * These support functions do all the actual work. They may seem * rather longish, but that's because I've commented them up. The * actual code is straight forward. * */ /* * BLST_NEXT_LEAF_ALLOC() - allocate the first few blocks in the next leaf. * * 'scan' is a leaf node, associated with a block containing 'blk'. * The next leaf node could be adjacent, or several nodes away if the * least common ancestor of 'scan' and its neighbor is several levels * up. Use 'blk' to determine how many meta-nodes lie between the * leaves. If the next leaf has enough initial bits set, clear them * and clear the bits in the meta nodes on the path up to the least * common ancestor to mark any subtrees made completely empty. */ static int blst_next_leaf_alloc(blmeta_t *scan, daddr_t blk, int count) { blmeta_t *next; daddr_t skip; u_daddr_t radix; int digit; next = scan + 1; blk += BLIST_BMAP_RADIX; radix = BLIST_BMAP_RADIX; while ((digit = ((blk / radix) & BLIST_META_MASK)) == 0 && (next->bm_bitmap & 1) == 1) { next++; radix *= BLIST_META_RADIX; } if (((next->bm_bitmap + 1) & ~((u_daddr_t)-1 << count)) != 0) { /* * The next leaf doesn't have enough free blocks at the * beginning to complete the spanning allocation. */ return (ENOMEM); } /* Clear the first 'count' bits in the next leaf to allocate. */ next->bm_bitmap &= (u_daddr_t)-1 << count; /* * Update bitmaps of next-ancestors, up to least common ancestor. */ skip = radix_to_skip(radix); while (radix != BLIST_BMAP_RADIX && next->bm_bitmap == 0) { (--next)->bm_bitmap ^= 1; radix /= BLIST_META_RADIX; } if (next->bm_bitmap == 0) scan[-digit * skip].bm_bitmap ^= (u_daddr_t)1 << digit; return (0); } /* * BLST_LEAF_ALLOC() - allocate at a leaf in the radix tree (a bitmap). * - * This is the core of the allocator and is optimized for the - * BLIST_BMAP_RADIX block allocation case. Otherwise, execution - * time is proportional to log2(count) + bitpos time. + * This function is the core of the allocator. Its execution time is + * proportional to log(count), plus height of the tree if the allocation + * crosses a leaf boundary. */ static daddr_t blst_leaf_alloc(blmeta_t *scan, daddr_t blk, int count) { - u_daddr_t mask; + u_daddr_t cursor_mask, mask; int count1, hi, lo, num_shifts, range1, range_ext; range1 = 0; count1 = count - 1; num_shifts = fls(count1); mask = scan->bm_bitmap; while ((-mask & ~mask) != 0 && num_shifts > 0) { /* * If bit i is set in mask, then bits in [i, i+range1] are set - * in scan->bm_bitmap. The value of range1 is equal to - * count1 >> num_shifts. Grow range and reduce num_shifts to 0, - * while preserving these invariants. The updates to mask leave - * fewer bits set, but each bit that remains set represents a - * longer string of consecutive bits set in scan->bm_bitmap. - * If more updates to mask cannot clear more bits, because mask - * is partitioned with all 0 bits preceding all 1 bits, the loop - * terminates immediately. + * in scan->bm_bitmap. The value of range1 is equal to count1 + * >> num_shifts. Grow range1 and reduce num_shifts to 0, + * while preserving these invariants. The updates to mask + * leave fewer bits set, but each bit that remains set + * represents a longer string of consecutive bits set in + * scan->bm_bitmap. If more updates to mask cannot clear more + * bits, because mask is partitioned with all 0 bits preceding + * all 1 bits, the loop terminates immediately. */ num_shifts--; range_ext = range1 + ((count1 >> num_shifts) & 1); /* * mask is a signed quantity for the shift because when it is * shifted right, the sign bit should copied; when the last * block of the leaf is free, pretend, for a while, that all the * blocks that follow it are also free. */ mask &= (daddr_t)mask >> range_ext; range1 += range_ext; } if (mask == 0) { /* * Update bighint. There is no allocation bigger than range1 * starting in this leaf. */ scan->bm_bighint = range1; return (SWAPBLK_NONE); } /* Discard any candidates that appear before blk. */ - mask &= (u_daddr_t)-1 << (blk & BLIST_BMAP_MASK); - if (mask == 0) - return (SWAPBLK_NONE); + if ((blk & BLIST_BMAP_MASK) != 0) { + cursor_mask = mask & bitrange(0, blk & BLIST_BMAP_MASK); + if (cursor_mask != 0) { + mask ^= cursor_mask; + if (mask == 0) + return (SWAPBLK_NONE); + /* + * Bighint change for last block allocation cannot + * assume that any other blocks are allocated, so the + * bighint cannot be reduced much. + */ + range1 = BLIST_MAX_ALLOC - 1; + } + blk &= ~BLIST_BMAP_MASK; + } + /* * The least significant set bit in mask marks the start of the first * available range of sufficient size. Clear all the bits but that one, * and then find its position. */ mask &= -mask; lo = bitpos(mask); hi = lo + count; if (hi > BLIST_BMAP_RADIX) { /* * An allocation within this leaf is impossible, so a successful * allocation depends on the next leaf providing some of the blocks. */ if (blst_next_leaf_alloc(scan, blk, hi - BLIST_BMAP_RADIX) != 0) /* * The hint cannot be updated, because the same * allocation request could be satisfied later, by this * leaf, if the state of the next leaf changes, and * without any changes to this leaf. */ return (SWAPBLK_NONE); hi = BLIST_BMAP_RADIX; } /* Set the bits of mask at position 'lo' and higher. */ mask = -mask; if (hi == BLIST_BMAP_RADIX) { /* * Update bighint. There is no allocation bigger than range1 * available in this leaf after this allocation completes. */ scan->bm_bighint = range1; } else { /* Clear the bits of mask at position 'hi' and higher. */ mask &= (u_daddr_t)-1 >> (BLIST_BMAP_RADIX - hi); } /* Clear the allocated bits from this leaf. */ scan->bm_bitmap &= ~mask; - return ((blk & ~BLIST_BMAP_MASK) + lo); + return (blk + lo); } /* * blist_meta_alloc() - allocate at a meta in the radix tree. * * Attempt to allocate at a meta node. If we can't, we update * bighint and return a failure. Updating bighint optimize future * calls that hit this node. We have to check for our collapse cases * and we have a few optimizations strewn in as well. */ static daddr_t blst_meta_alloc(blmeta_t *scan, daddr_t cursor, daddr_t count, u_daddr_t radix) { daddr_t blk, i, r, skip; u_daddr_t bit, mask; bool scan_from_start; int digit; if (radix == BLIST_BMAP_RADIX) return (blst_leaf_alloc(scan, cursor, count)); blk = cursor & -radix; scan_from_start = (cursor == blk); radix /= BLIST_META_RADIX; skip = radix_to_skip(radix); mask = scan->bm_bitmap; /* Discard any candidates that appear before cursor. */ digit = (cursor / radix) & BLIST_META_MASK; mask &= (u_daddr_t)-1 << digit; if (mask == 0) return (SWAPBLK_NONE); /* * If the first try is for a block that includes the cursor, pre-undo * the digit * radix offset in the first call; otherwise, ignore the * cursor entirely. */ if (((mask >> digit) & 1) == 1) cursor -= digit * radix; else cursor = blk; /* * Examine the nonempty subtree associated with each bit set in mask. */ do { bit = mask & -mask; digit = bitpos(bit); i = 1 + digit * skip; if (count <= scan[i].bm_bighint) { /* * The allocation might fit beginning in the i'th subtree. */ r = blst_meta_alloc(&scan[i], cursor + digit * radix, count, radix); if (r != SWAPBLK_NONE) { if (scan[i].bm_bitmap == 0) scan->bm_bitmap ^= bit; return (r); } } cursor = blk; } while ((mask ^= bit) != 0); /* * We couldn't allocate count in this subtree. If the whole tree was * scanned, and the last tree node is allocated, update bighint. */ if (scan_from_start && !(digit == BLIST_META_RADIX - 1 && scan[i].bm_bighint == BLIST_MAX_ALLOC)) scan->bm_bighint = count - 1; return (SWAPBLK_NONE); } /* * BLST_LEAF_FREE() - free allocated block from leaf bitmap * */ static void blst_leaf_free(blmeta_t *scan, daddr_t blk, int count) { u_daddr_t mask; /* * free some data in this bitmap * mask=0000111111111110000 * \_________/\__/ * count n */ mask = bitrange(blk & BLIST_BMAP_MASK, count); if (scan->bm_bitmap & mask) panic("freeing free block"); scan->bm_bitmap |= mask; } /* * BLST_META_FREE() - free allocated blocks from radix tree meta info * * This support routine frees a range of blocks from the bitmap. * The range must be entirely enclosed by this radix node. If a * meta node, we break the range down recursively to free blocks * in subnodes (which means that this code can free an arbitrary * range whereas the allocation code cannot allocate an arbitrary * range). */ static void blst_meta_free(blmeta_t *scan, daddr_t freeBlk, daddr_t count, u_daddr_t radix) { daddr_t blk, endBlk, i, skip; int digit, endDigit; /* * We could probably do a better job here. We are required to make * bighint at least as large as the biggest allocable block of data. * If we just shoehorn it, a little extra overhead will be incurred * on the next allocation (but only that one typically). */ scan->bm_bighint = BLIST_MAX_ALLOC; if (radix == BLIST_BMAP_RADIX) return (blst_leaf_free(scan, freeBlk, count)); endBlk = ummin(freeBlk + count, (freeBlk + radix) & -radix); radix /= BLIST_META_RADIX; skip = radix_to_skip(radix); blk = freeBlk & -radix; digit = (blk / radix) & BLIST_META_MASK; endDigit = 1 + (((endBlk - 1) / radix) & BLIST_META_MASK); scan->bm_bitmap |= bitrange(digit, endDigit - digit); for (i = 1 + digit * skip; blk < endBlk; i += skip) { blk += radix; count = ummin(blk, endBlk) - freeBlk; blst_meta_free(&scan[i], freeBlk, count, radix); freeBlk = blk; } } /* * BLST_COPY() - copy one radix tree to another * * Locates free space in the source tree and frees it in the destination * tree. The space may not already be free in the destination. */ static void blst_copy(blmeta_t *scan, daddr_t blk, daddr_t radix, blist_t dest, daddr_t count) { daddr_t endBlk, i, skip; /* * Leaf node */ if (radix == BLIST_BMAP_RADIX) { u_daddr_t v = scan->bm_bitmap; if (v == (u_daddr_t)-1) { blist_free(dest, blk, count); } else if (v != 0) { int i; for (i = 0; i < count; ++i) { if (v & ((u_daddr_t)1 << i)) blist_free(dest, blk + i, 1); } } return; } /* * Meta node */ if (scan->bm_bitmap == 0) { /* * Source all allocated, leave dest allocated */ return; } endBlk = blk + count; radix /= BLIST_META_RADIX; skip = radix_to_skip(radix); for (i = 1; blk < endBlk; i += skip) { blk += radix; count = radix; if (blk >= endBlk) count -= blk - endBlk; blst_copy(&scan[i], blk - radix, radix, dest, count); } } /* * BLST_LEAF_FILL() - allocate specific blocks in leaf bitmap * * This routine allocates all blocks in the specified range * regardless of any existing allocations in that range. Returns * the number of blocks allocated by the call. */ static daddr_t blst_leaf_fill(blmeta_t *scan, daddr_t blk, int count) { daddr_t nblks; u_daddr_t mask; mask = bitrange(blk & BLIST_BMAP_MASK, count); /* Count the number of blocks that we are allocating. */ nblks = bitcount64(scan->bm_bitmap & mask); scan->bm_bitmap &= ~mask; return (nblks); } /* * BLIST_META_FILL() - allocate specific blocks at a meta node * * This routine allocates the specified range of blocks, * regardless of any existing allocations in the range. The * range must be within the extent of this node. Returns the * number of blocks allocated by the call. */ static daddr_t blst_meta_fill(blmeta_t *scan, daddr_t allocBlk, daddr_t count, u_daddr_t radix) { daddr_t blk, endBlk, i, nblks, skip; int digit; if (radix == BLIST_BMAP_RADIX) return (blst_leaf_fill(scan, allocBlk, count)); endBlk = ummin(allocBlk + count, (allocBlk + radix) & -radix); radix /= BLIST_META_RADIX; skip = radix_to_skip(radix); blk = allocBlk & -radix; nblks = 0; while (blk < endBlk) { digit = (blk / radix) & BLIST_META_MASK; i = 1 + digit * skip; blk += radix; count = ummin(blk, endBlk) - allocBlk; nblks += blst_meta_fill(&scan[i], allocBlk, count, radix); if (scan[i].bm_bitmap == 0) scan->bm_bitmap &= ~((u_daddr_t)1 << digit); allocBlk = blk; } return (nblks); } #ifdef BLIST_DEBUG static void blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, int tab) { daddr_t skip; u_daddr_t bit, mask; int digit; if (radix == BLIST_BMAP_RADIX) { printf( "%*.*s(%08llx,%lld): bitmap %0*llx big=%lld\n", tab, tab, "", (long long)blk, (long long)radix, 1 + (BLIST_BMAP_RADIX - 1) / 4, (long long)scan->bm_bitmap, (long long)scan->bm_bighint ); return; } printf( "%*.*s(%08llx): subtree (%lld/%lld) bitmap %0*llx big=%lld {\n", tab, tab, "", (long long)blk, (long long)radix, (long long)radix, 1 + (BLIST_META_RADIX - 1) / 4, (long long)scan->bm_bitmap, (long long)scan->bm_bighint ); radix /= BLIST_META_RADIX; skip = radix_to_skip(radix); tab += 4; mask = scan->bm_bitmap; /* Examine the nonempty subtree associated with each bit set in mask */ do { bit = mask & -mask; digit = bitpos(bit); blst_radix_print(&scan[1 + digit * skip], blk + digit * radix, radix, tab); } while ((mask ^= bit) != 0); tab -= 4; printf( "%*.*s}\n", tab, tab, "" ); } #endif #ifdef BLIST_DEBUG int main(int ac, char **av) { int size = BLIST_META_RADIX * BLIST_BMAP_RADIX; int i; blist_t bl; struct sbuf *s; for (i = 1; i < ac; ++i) { const char *ptr = av[i]; if (*ptr != '-') { size = strtol(ptr, NULL, 0); continue; } ptr += 2; fprintf(stderr, "Bad option: %s\n", ptr - 2); exit(1); } bl = blist_create(size, M_WAITOK); blist_free(bl, 0, size); for (;;) { char buf[1024]; long long da = 0; long long count = 0; printf("%lld/%lld/%lld> ", (long long)blist_avail(bl), (long long)size, (long long)bl->bl_radix); fflush(stdout); if (fgets(buf, sizeof(buf), stdin) == NULL) break; switch(buf[0]) { case 'r': if (sscanf(buf + 1, "%lld", &count) == 1) { blist_resize(&bl, count, 1, M_WAITOK); } else { printf("?\n"); } case 'p': blist_print(bl); break; case 's': s = sbuf_new_auto(); blist_stats(bl, s); sbuf_finish(s); printf("%s", sbuf_data(s)); sbuf_delete(s); break; case 'a': if (sscanf(buf + 1, "%lld", &count) == 1) { daddr_t blk = blist_alloc(bl, count); printf(" R=%08llx\n", (long long)blk); } else { printf("?\n"); } break; case 'f': if (sscanf(buf + 1, "%llx %lld", &da, &count) == 2) { blist_free(bl, da, count); } else { printf("?\n"); } break; case 'l': if (sscanf(buf + 1, "%llx %lld", &da, &count) == 2) { printf(" n=%jd\n", (intmax_t)blist_fill(bl, da, count)); } else { printf("?\n"); } break; case '?': case 'h': puts( "p -print\n" "s -stats\n" "a %d -allocate\n" "f %x %d -free\n" "l %x %d -fill\n" "r %d -resize\n" "h/? -help" ); break; default: printf("?\n"); break; } } return(0); } void panic(const char *ctl, ...) { va_list va; va_start(va, ctl); vfprintf(stderr, ctl, va); fprintf(stderr, "\n"); va_end(va); exit(1); } #endif Index: projects/clang700-import/sys/kern/vfs_syscalls.c =================================================================== --- projects/clang700-import/sys/kern/vfs_syscalls.c (revision 341812) +++ projects/clang700-import/sys/kern/vfs_syscalls.c (revision 341813) @@ -1,4753 +1,4753 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KTRACE #include #endif #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_FADVISE, "fadvise", "posix_fadvise(2) information"); SDT_PROVIDER_DEFINE(vfs); SDT_PROBE_DEFINE2(vfs, , stat, mode, "char *", "int"); SDT_PROBE_DEFINE2(vfs, , stat, reg, "char *", "int"); static int kern_chflagsat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, u_long flags, int atflag); static int setfflags(struct thread *td, struct vnode *, u_long); static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); static int getutimens(const struct timespec *, enum uio_seg, struct timespec *, int *); static int setutimes(struct thread *td, struct vnode *, const struct timespec *, int, int); static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred, struct thread *td); static int kern_fhlinkat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, fhandle_t *fhp); static int kern_getfhat(struct thread *td, int flags, int fd, const char *path, enum uio_seg pathseg, fhandle_t *fhp); static int kern_readlink_vp(struct vnode *vp, char *buf, enum uio_seg bufseg, size_t count, struct thread *td); static int kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path, enum uio_seg segflag); /* * Sync each mounted filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct sync_args { int dummy; }; #endif /* ARGSUSED */ int sys_sync(struct thread *td, struct sync_args *uap) { struct mount *mp, *nmp; int save; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, MBF_NOWAIT | MBF_MNTLSTLOCK)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if ((mp->mnt_flag & MNT_RDONLY) == 0 && vn_start_write(NULL, &mp, V_NOWAIT) == 0) { save = curthread_pflags_set(TDP_SYNCIO); vfs_msync(mp, MNT_NOWAIT); VFS_SYNC(mp, MNT_NOWAIT); curthread_pflags_restore(save); vn_finished_write(mp); } mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp); } mtx_unlock(&mountlist_mtx); return (0); } /* * Change filesystem quotas. */ #ifndef _SYS_SYSPROTO_H_ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; #endif int sys_quotactl(struct thread *td, struct quotactl_args *uap) { struct mount *mp; struct nameidata nd; int error; AUDIT_ARG_CMD(uap->cmd); AUDIT_ARG_UID(uap->uid); if (!prison_allow(td->td_ucred, PR_ALLOW_QUOTAS)) return (EPERM); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); mp = nd.ni_vp->v_mount; vfs_ref(mp); vput(nd.ni_vp); error = vfs_busy(mp, 0); vfs_rel(mp); if (error != 0) return (error); error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg); /* * Since quota on operation typically needs to open quota * file, the Q_QUOTAON handler needs to unbusy the mount point * before calling into namei. Otherwise, unmount might be * started between two vfs_busy() invocations (first is our, * second is from mount point cross-walk code in lookup()), * causing deadlock. * * Require that Q_QUOTAON handles the vfs_busy() reference on * its own, always returning with ubusied mount point. */ if ((uap->cmd >> SUBCMDSHIFT) != Q_QUOTAON && (uap->cmd >> SUBCMDSHIFT) != Q_QUOTAOFF) vfs_unbusy(mp); return (error); } /* * Used by statfs conversion routines to scale the block size up if * necessary so that all of the block counts are <= 'max_size'. Note * that 'max_size' should be a bitmask, i.e. 2^n - 1 for some non-zero * value of 'n'. */ void statfs_scale_blocks(struct statfs *sf, long max_size) { uint64_t count; int shift; KASSERT(powerof2(max_size + 1), ("%s: invalid max_size", __func__)); /* * Attempt to scale the block counts to give a more accurate * overview to userland of the ratio of free space to used * space. To do this, find the largest block count and compute * a divisor that lets it fit into a signed integer <= max_size. */ if (sf->f_bavail < 0) count = -sf->f_bavail; else count = sf->f_bavail; count = MAX(sf->f_blocks, MAX(sf->f_bfree, count)); if (count <= max_size) return; count >>= flsl(max_size); shift = 0; while (count > 0) { shift++; count >>=1; } sf->f_bsize <<= shift; sf->f_blocks >>= shift; sf->f_bfree >>= shift; sf->f_bavail >>= shift; } static int kern_do_statfs(struct thread *td, struct mount *mp, struct statfs *buf) { struct statfs *sp; int error; if (mp == NULL) return (EBADF); error = vfs_busy(mp, 0); vfs_rel(mp); if (error != 0) return (error); #ifdef MAC error = mac_mount_check_stat(td->td_ucred, mp); if (error != 0) goto out; #endif /* * Set these in case the underlying filesystem fails to do so. */ sp = &mp->mnt_stat; sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp); if (error != 0) goto out; *buf = *sp; if (priv_check(td, PRIV_VFS_GENERATION)) { buf->f_fsid.val[0] = buf->f_fsid.val[1] = 0; prison_enforce_statfs(td->td_ucred, mp, buf); } out: vfs_unbusy(mp); return (error); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct statfs_args { char *path; struct statfs *buf; }; #endif int sys_statfs(struct thread *td, struct statfs_args *uap) { struct statfs *sfp; int error; sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_statfs(td, uap->path, UIO_USERSPACE, sfp); if (error == 0) error = copyout(sfp, uap->buf, sizeof(struct statfs)); free(sfp, M_STATFS); return (error); } int kern_statfs(struct thread *td, const char *path, enum uio_seg pathseg, struct statfs *buf) { struct mount *mp; struct nameidata nd; int error; NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, td); error = namei(&nd); if (error != 0) return (error); mp = nd.ni_vp->v_mount; vfs_ref(mp); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); return (kern_do_statfs(td, mp, buf)); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct fstatfs_args { int fd; struct statfs *buf; }; #endif int sys_fstatfs(struct thread *td, struct fstatfs_args *uap) { struct statfs *sfp; int error; sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_fstatfs(td, uap->fd, sfp); if (error == 0) error = copyout(sfp, uap->buf, sizeof(struct statfs)); free(sfp, M_STATFS); return (error); } int kern_fstatfs(struct thread *td, int fd, struct statfs *buf) { struct file *fp; struct mount *mp; struct vnode *vp; int error; AUDIT_ARG_FD(fd); error = getvnode(td, fd, &cap_fstatfs_rights, &fp); if (error != 0) return (error); vp = fp->f_vnode; vn_lock(vp, LK_SHARED | LK_RETRY); #ifdef AUDIT AUDIT_ARG_VNODE1(vp); #endif mp = vp->v_mount; if (mp != NULL) vfs_ref(mp); VOP_UNLOCK(vp, 0); fdrop(fp, td); return (kern_do_statfs(td, mp, buf)); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct getfsstat_args { struct statfs *buf; long bufsize; int mode; }; #endif int sys_getfsstat(struct thread *td, struct getfsstat_args *uap) { size_t count; int error; if (uap->bufsize < 0 || uap->bufsize > SIZE_MAX) return (EINVAL); error = kern_getfsstat(td, &uap->buf, uap->bufsize, &count, UIO_USERSPACE, uap->mode); if (error == 0) td->td_retval[0] = count; return (error); } /* * If (bufsize > 0 && bufseg == UIO_SYSSPACE) * The caller is responsible for freeing memory which will be allocated * in '*buf'. */ int kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize, size_t *countp, enum uio_seg bufseg, int mode) { struct mount *mp, *nmp; struct statfs *sfsp, *sp, *sptmp, *tofree; size_t count, maxcount; int error; switch (mode) { case MNT_WAIT: case MNT_NOWAIT: break; default: if (bufseg == UIO_SYSSPACE) *buf = NULL; return (EINVAL); } restart: maxcount = bufsize / sizeof(struct statfs); if (bufsize == 0) { sfsp = NULL; tofree = NULL; } else if (bufseg == UIO_USERSPACE) { sfsp = *buf; tofree = NULL; } else /* if (bufseg == UIO_SYSSPACE) */ { count = 0; mtx_lock(&mountlist_mtx); TAILQ_FOREACH(mp, &mountlist, mnt_list) { count++; } mtx_unlock(&mountlist_mtx); if (maxcount > count) maxcount = count; tofree = sfsp = *buf = malloc(maxcount * sizeof(struct statfs), M_STATFS, M_WAITOK); } count = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (prison_canseemount(td->td_ucred, mp) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #ifdef MAC if (mac_mount_check_stat(td->td_ucred, mp) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } #endif if (mode == MNT_WAIT) { if (vfs_busy(mp, MBF_MNTLSTLOCK) != 0) { /* * If vfs_busy() failed, and MBF_NOWAIT * wasn't passed, then the mp is gone. * Furthermore, because of MBF_MNTLSTLOCK, * the mountlist_mtx was dropped. We have * no other choice than to start over. */ mtx_unlock(&mountlist_mtx); free(tofree, M_STATFS); goto restart; } } else { if (vfs_busy(mp, MBF_NOWAIT | MBF_MNTLSTLOCK) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } } if (sfsp != NULL && count < maxcount) { sp = &mp->mnt_stat; /* * Set these in case the underlying filesystem * fails to do so. */ sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; /* * If MNT_NOWAIT is specified, do not refresh * the fsstat cache. */ if (mode != MNT_NOWAIT) { error = VFS_STATFS(mp, sp); if (error != 0) { mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp); continue; } } if (priv_check(td, PRIV_VFS_GENERATION)) { sptmp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); *sptmp = *sp; sptmp->f_fsid.val[0] = sptmp->f_fsid.val[1] = 0; prison_enforce_statfs(td->td_ucred, mp, sptmp); sp = sptmp; } else sptmp = NULL; if (bufseg == UIO_SYSSPACE) { bcopy(sp, sfsp, sizeof(*sp)); free(sptmp, M_STATFS); } else /* if (bufseg == UIO_USERSPACE) */ { error = copyout(sp, sfsp, sizeof(*sp)); free(sptmp, M_STATFS); if (error != 0) { vfs_unbusy(mp); return (error); } } sfsp++; } count++; mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp); } mtx_unlock(&mountlist_mtx); if (sfsp != NULL && count > maxcount) *countp = maxcount; else *countp = count; return (0); } #ifdef COMPAT_FREEBSD4 /* * Get old format filesystem statistics. */ static void freebsd4_cvtstatfs(struct statfs *, struct ostatfs *); #ifndef _SYS_SYSPROTO_H_ struct freebsd4_statfs_args { char *path; struct ostatfs *buf; }; #endif int freebsd4_statfs(struct thread *td, struct freebsd4_statfs_args *uap) { struct ostatfs osb; struct statfs *sfp; int error; sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_statfs(td, uap->path, UIO_USERSPACE, sfp); if (error == 0) { freebsd4_cvtstatfs(sfp, &osb); error = copyout(&osb, uap->buf, sizeof(osb)); } free(sfp, M_STATFS); return (error); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_fstatfs_args { int fd; struct ostatfs *buf; }; #endif int freebsd4_fstatfs(struct thread *td, struct freebsd4_fstatfs_args *uap) { struct ostatfs osb; struct statfs *sfp; int error; sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_fstatfs(td, uap->fd, sfp); if (error == 0) { freebsd4_cvtstatfs(sfp, &osb); error = copyout(&osb, uap->buf, sizeof(osb)); } free(sfp, M_STATFS); return (error); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_getfsstat_args { struct ostatfs *buf; long bufsize; int mode; }; #endif int freebsd4_getfsstat(struct thread *td, struct freebsd4_getfsstat_args *uap) { struct statfs *buf, *sp; struct ostatfs osb; size_t count, size; int error; if (uap->bufsize < 0) return (EINVAL); count = uap->bufsize / sizeof(struct ostatfs); if (count > SIZE_MAX / sizeof(struct statfs)) return (EINVAL); size = count * sizeof(struct statfs); error = kern_getfsstat(td, &buf, size, &count, UIO_SYSSPACE, uap->mode); if (error == 0) td->td_retval[0] = count; if (size != 0) { sp = buf; while (count != 0 && error == 0) { freebsd4_cvtstatfs(sp, &osb); error = copyout(&osb, uap->buf, sizeof(osb)); sp++; uap->buf++; count--; } free(buf, M_STATFS); } return (error); } /* * Implement fstatfs() for (NFS) file handles. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd4_fhstatfs_args { struct fhandle *u_fhp; struct ostatfs *buf; }; #endif int freebsd4_fhstatfs(struct thread *td, struct freebsd4_fhstatfs_args *uap) { struct ostatfs osb; struct statfs *sfp; fhandle_t fh; int error; error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); if (error != 0) return (error); sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_fhstatfs(td, fh, sfp); if (error == 0) { freebsd4_cvtstatfs(sfp, &osb); error = copyout(&osb, uap->buf, sizeof(osb)); } free(sfp, M_STATFS); return (error); } /* * Convert a new format statfs structure to an old format statfs structure. */ static void freebsd4_cvtstatfs(struct statfs *nsp, struct ostatfs *osp) { statfs_scale_blocks(nsp, LONG_MAX); bzero(osp, sizeof(*osp)); osp->f_bsize = nsp->f_bsize; osp->f_iosize = MIN(nsp->f_iosize, LONG_MAX); osp->f_blocks = nsp->f_blocks; osp->f_bfree = nsp->f_bfree; osp->f_bavail = nsp->f_bavail; osp->f_files = MIN(nsp->f_files, LONG_MAX); osp->f_ffree = MIN(nsp->f_ffree, LONG_MAX); osp->f_owner = nsp->f_owner; osp->f_type = nsp->f_type; osp->f_flags = nsp->f_flags; osp->f_syncwrites = MIN(nsp->f_syncwrites, LONG_MAX); osp->f_asyncwrites = MIN(nsp->f_asyncwrites, LONG_MAX); osp->f_syncreads = MIN(nsp->f_syncreads, LONG_MAX); osp->f_asyncreads = MIN(nsp->f_asyncreads, LONG_MAX); strlcpy(osp->f_fstypename, nsp->f_fstypename, MIN(MFSNAMELEN, OMFSNAMELEN)); strlcpy(osp->f_mntonname, nsp->f_mntonname, MIN(MNAMELEN, OMNAMELEN)); strlcpy(osp->f_mntfromname, nsp->f_mntfromname, MIN(MNAMELEN, OMNAMELEN)); osp->f_fsid = nsp->f_fsid; } #endif /* COMPAT_FREEBSD4 */ #if defined(COMPAT_FREEBSD11) /* * Get old format filesystem statistics. */ static void freebsd11_cvtstatfs(struct statfs *, struct freebsd11_statfs *); int freebsd11_statfs(struct thread *td, struct freebsd11_statfs_args *uap) { struct freebsd11_statfs osb; struct statfs *sfp; int error; sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_statfs(td, uap->path, UIO_USERSPACE, sfp); if (error == 0) { freebsd11_cvtstatfs(sfp, &osb); error = copyout(&osb, uap->buf, sizeof(osb)); } free(sfp, M_STATFS); return (error); } /* * Get filesystem statistics. */ int freebsd11_fstatfs(struct thread *td, struct freebsd11_fstatfs_args *uap) { struct freebsd11_statfs osb; struct statfs *sfp; int error; sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_fstatfs(td, uap->fd, sfp); if (error == 0) { freebsd11_cvtstatfs(sfp, &osb); error = copyout(&osb, uap->buf, sizeof(osb)); } free(sfp, M_STATFS); return (error); } /* * Get statistics on all filesystems. */ int freebsd11_getfsstat(struct thread *td, struct freebsd11_getfsstat_args *uap) { struct freebsd11_statfs osb; struct statfs *buf, *sp; size_t count, size; int error; count = uap->bufsize / sizeof(struct ostatfs); size = count * sizeof(struct statfs); error = kern_getfsstat(td, &buf, size, &count, UIO_SYSSPACE, uap->mode); if (error == 0) td->td_retval[0] = count; if (size > 0) { sp = buf; while (count > 0 && error == 0) { freebsd11_cvtstatfs(sp, &osb); error = copyout(&osb, uap->buf, sizeof(osb)); sp++; uap->buf++; count--; } free(buf, M_STATFS); } return (error); } /* * Implement fstatfs() for (NFS) file handles. */ int freebsd11_fhstatfs(struct thread *td, struct freebsd11_fhstatfs_args *uap) { struct freebsd11_statfs osb; struct statfs *sfp; fhandle_t fh; int error; error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); if (error) return (error); sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_fhstatfs(td, fh, sfp); if (error == 0) { freebsd11_cvtstatfs(sfp, &osb); error = copyout(&osb, uap->buf, sizeof(osb)); } free(sfp, M_STATFS); return (error); } /* * Convert a new format statfs structure to an old format statfs structure. */ static void freebsd11_cvtstatfs(struct statfs *nsp, struct freebsd11_statfs *osp) { bzero(osp, sizeof(*osp)); osp->f_version = FREEBSD11_STATFS_VERSION; osp->f_type = nsp->f_type; osp->f_flags = nsp->f_flags; osp->f_bsize = nsp->f_bsize; osp->f_iosize = nsp->f_iosize; osp->f_blocks = nsp->f_blocks; osp->f_bfree = nsp->f_bfree; osp->f_bavail = nsp->f_bavail; osp->f_files = nsp->f_files; osp->f_ffree = nsp->f_ffree; osp->f_syncwrites = nsp->f_syncwrites; osp->f_asyncwrites = nsp->f_asyncwrites; osp->f_syncreads = nsp->f_syncreads; osp->f_asyncreads = nsp->f_asyncreads; osp->f_namemax = nsp->f_namemax; osp->f_owner = nsp->f_owner; osp->f_fsid = nsp->f_fsid; strlcpy(osp->f_fstypename, nsp->f_fstypename, MIN(MFSNAMELEN, sizeof(osp->f_fstypename))); strlcpy(osp->f_mntonname, nsp->f_mntonname, MIN(MNAMELEN, sizeof(osp->f_mntonname))); strlcpy(osp->f_mntfromname, nsp->f_mntfromname, MIN(MNAMELEN, sizeof(osp->f_mntfromname))); } #endif /* COMPAT_FREEBSD11 */ /* * Change current working directory to a given file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchdir_args { int fd; }; #endif int sys_fchdir(struct thread *td, struct fchdir_args *uap) { struct vnode *vp, *tdp; struct mount *mp; struct file *fp; int error; AUDIT_ARG_FD(uap->fd); error = getvnode(td, uap->fd, &cap_fchdir_rights, &fp); if (error != 0) return (error); vp = fp->f_vnode; vrefact(vp); fdrop(fp, td); vn_lock(vp, LK_SHARED | LK_RETRY); AUDIT_ARG_VNODE1(vp); error = change_dir(vp, td); while (!error && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, 0)) continue; error = VFS_ROOT(mp, LK_SHARED, &tdp); vfs_unbusy(mp); if (error != 0) break; vput(vp); vp = tdp; } if (error != 0) { vput(vp); return (error); } VOP_UNLOCK(vp, 0); pwd_chdir(td, vp); return (0); } /* * Change current working directory (``.''). */ #ifndef _SYS_SYSPROTO_H_ struct chdir_args { char *path; }; #endif int sys_chdir(struct thread *td, struct chdir_args *uap) { return (kern_chdir(td, uap->path, UIO_USERSPACE)); } int kern_chdir(struct thread *td, const char *path, enum uio_seg pathseg) { struct nameidata nd; int error; NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); if ((error = change_dir(nd.ni_vp, td)) != 0) { vput(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); return (error); } VOP_UNLOCK(nd.ni_vp, 0); NDFREE(&nd, NDF_ONLY_PNBUF); pwd_chdir(td, nd.ni_vp); return (0); } /* * Change notion of root (``/'') directory. */ #ifndef _SYS_SYSPROTO_H_ struct chroot_args { char *path; }; #endif int sys_chroot(struct thread *td, struct chroot_args *uap) { struct nameidata nd; int error; error = priv_check(td, PRIV_VFS_CHROOT); if (error != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error != 0) goto error; error = change_dir(nd.ni_vp, td); if (error != 0) goto e_vunlock; #ifdef MAC error = mac_vnode_check_chroot(td->td_ucred, nd.ni_vp); if (error != 0) goto e_vunlock; #endif VOP_UNLOCK(nd.ni_vp, 0); error = pwd_chroot(td, nd.ni_vp); vrele(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); return (error); e_vunlock: vput(nd.ni_vp); error: NDFREE(&nd, NDF_ONLY_PNBUF); return (error); } /* * Common routine for chroot and chdir. Callers must provide a locked vnode * instance. */ int change_dir(struct vnode *vp, struct thread *td) { #ifdef MAC int error; #endif ASSERT_VOP_LOCKED(vp, "change_dir(): vp not locked"); if (vp->v_type != VDIR) return (ENOTDIR); #ifdef MAC error = mac_vnode_check_chdir(td->td_ucred, vp); if (error != 0) return (error); #endif return (VOP_ACCESS(vp, VEXEC, td->td_ucred, td)); } static __inline void flags_to_rights(int flags, cap_rights_t *rightsp) { if (flags & O_EXEC) { cap_rights_set(rightsp, CAP_FEXECVE); } else { switch ((flags & O_ACCMODE)) { case O_RDONLY: cap_rights_set(rightsp, CAP_READ); break; case O_RDWR: cap_rights_set(rightsp, CAP_READ); /* FALLTHROUGH */ case O_WRONLY: cap_rights_set(rightsp, CAP_WRITE); if (!(flags & (O_APPEND | O_TRUNC))) cap_rights_set(rightsp, CAP_SEEK); break; } } if (flags & O_CREAT) cap_rights_set(rightsp, CAP_CREATE); if (flags & O_TRUNC) cap_rights_set(rightsp, CAP_FTRUNCATE); if (flags & (O_SYNC | O_FSYNC)) cap_rights_set(rightsp, CAP_FSYNC); if (flags & (O_EXLOCK | O_SHLOCK)) cap_rights_set(rightsp, CAP_FLOCK); } /* * Check permissions, allocate an open file structure, and call the device * open routine if any. */ #ifndef _SYS_SYSPROTO_H_ struct open_args { char *path; int flags; int mode; }; #endif int sys_open(struct thread *td, struct open_args *uap) { return (kern_openat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->flags, uap->mode)); } #ifndef _SYS_SYSPROTO_H_ struct openat_args { int fd; char *path; int flag; int mode; }; #endif int sys_openat(struct thread *td, struct openat_args *uap) { AUDIT_ARG_FD(uap->fd); return (kern_openat(td, uap->fd, uap->path, UIO_USERSPACE, uap->flag, uap->mode)); } int kern_openat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, int flags, int mode) { struct proc *p = td->td_proc; struct filedesc *fdp = p->p_fd; struct file *fp; struct vnode *vp; struct nameidata nd; cap_rights_t rights; int cmode, error, indx; indx = -1; AUDIT_ARG_FFLAGS(flags); AUDIT_ARG_MODE(mode); cap_rights_init(&rights, CAP_LOOKUP); flags_to_rights(flags, &rights); /* * Only one of the O_EXEC, O_RDONLY, O_WRONLY and O_RDWR flags * may be specified. */ if (flags & O_EXEC) { if (flags & O_ACCMODE) return (EINVAL); } else if ((flags & O_ACCMODE) == O_ACCMODE) { return (EINVAL); } else { flags = FFLAGS(flags); } /* * Allocate a file structure. The descriptor to reference it * is allocated and set by finstall() below. */ error = falloc_noinstall(td, &fp); if (error != 0) return (error); /* * An extra reference on `fp' has been held for us by * falloc_noinstall(). */ /* Set the flags early so the finit in devfs can pick them up. */ fp->f_flag = flags & FMASK; cmode = ((mode & ~fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT; NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | AUDITVNODE1, pathseg, path, fd, &rights, td); td->td_dupfd = -1; /* XXX check for fdopen */ error = vn_open(&nd, &flags, cmode, fp); if (error != 0) { /* * If the vn_open replaced the method vector, something * wonderous happened deep below and we just pass it up * pretending we know what we do. */ if (error == ENXIO && fp->f_ops != &badfileops) goto success; /* * Handle special fdopen() case. bleh. * * Don't do this for relative (capability) lookups; we don't * understand exactly what would happen, and we don't think * that it ever should. */ if ((nd.ni_lcf & NI_LCF_STRICTRELATIVE) == 0 && (error == ENODEV || error == ENXIO) && td->td_dupfd >= 0) { error = dupfdopen(td, fdp, td->td_dupfd, flags, error, &indx); if (error == 0) goto success; } goto bad; } td->td_dupfd = 0; NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; /* * Store the vnode, for any f_type. Typically, the vnode use * count is decremented by direct call to vn_closefile() for * files that switched type in the cdevsw fdopen() method. */ fp->f_vnode = vp; /* * If the file wasn't claimed by devfs bind it to the normal * vnode operations here. */ if (fp->f_ops == &badfileops) { KASSERT(vp->v_type != VFIFO, ("Unexpected fifo.")); fp->f_seqcount = 1; finit(fp, (flags & FMASK) | (fp->f_flag & FHASLOCK), DTYPE_VNODE, vp, &vnops); } VOP_UNLOCK(vp, 0); if (flags & O_TRUNC) { error = fo_truncate(fp, 0, td->td_ucred, td); if (error != 0) goto bad; } success: /* * If we haven't already installed the FD (for dupfdopen), do so now. */ if (indx == -1) { struct filecaps *fcaps; #ifdef CAPABILITIES if ((nd.ni_lcf & NI_LCF_STRICTRELATIVE) != 0) fcaps = &nd.ni_filecaps; else #endif fcaps = NULL; error = finstall(td, fp, &indx, flags, fcaps); /* On success finstall() consumes fcaps. */ if (error != 0) { filecaps_free(&nd.ni_filecaps); goto bad; } } else { filecaps_free(&nd.ni_filecaps); } /* * Release our private reference, leaving the one associated with * the descriptor table intact. */ fdrop(fp, td); td->td_retval[0] = indx; return (0); bad: KASSERT(indx == -1, ("indx=%d, should be -1", indx)); fdrop(fp, td); return (error); } #ifdef COMPAT_43 /* * Create a file. */ #ifndef _SYS_SYSPROTO_H_ struct ocreat_args { char *path; int mode; }; #endif int ocreat(struct thread *td, struct ocreat_args *uap) { return (kern_openat(td, AT_FDCWD, uap->path, UIO_USERSPACE, O_WRONLY | O_CREAT | O_TRUNC, uap->mode)); } #endif /* COMPAT_43 */ /* * Create a special file. */ #ifndef _SYS_SYSPROTO_H_ struct mknodat_args { int fd; char *path; mode_t mode; dev_t dev; }; #endif int sys_mknodat(struct thread *td, struct mknodat_args *uap) { return (kern_mknodat(td, uap->fd, uap->path, UIO_USERSPACE, uap->mode, uap->dev)); } #if defined(COMPAT_FREEBSD11) int freebsd11_mknod(struct thread *td, struct freebsd11_mknod_args *uap) { return (kern_mknodat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->mode, uap->dev)); } int freebsd11_mknodat(struct thread *td, struct freebsd11_mknodat_args *uap) { return (kern_mknodat(td, uap->fd, uap->path, UIO_USERSPACE, uap->mode, uap->dev)); } #endif /* COMPAT_FREEBSD11 */ int kern_mknodat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, int mode, dev_t dev) { struct vnode *vp; struct mount *mp; struct vattr vattr; struct nameidata nd; int error, whiteout = 0; AUDIT_ARG_MODE(mode); AUDIT_ARG_DEV(dev); switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: error = priv_check(td, PRIV_VFS_MKNOD_DEV); if (error == 0 && dev == VNOVAL) error = EINVAL; break; case S_IFWHT: error = priv_check(td, PRIV_VFS_MKNOD_WHT); break; case S_IFIFO: if (dev == 0) return (kern_mkfifoat(td, fd, path, pathseg, mode)); /* FALLTHROUGH */ default: error = EINVAL; break; } if (error != 0) return (error); restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1 | NOCACHE, pathseg, path, fd, &cap_mknodat_rights, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); if (vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(vp); return (EEXIST); } else { VATTR_NULL(&vattr); vattr.va_mode = (mode & ALLPERMS) & ~td->td_proc->p_fd->fd_cmask; vattr.va_rdev = dev; whiteout = 0; switch (mode & S_IFMT) { case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; case S_IFWHT: whiteout = 1; break; default: panic("kern_mknod: invalid mode"); } } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } #ifdef MAC if (error == 0 && !whiteout) error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); #endif if (error == 0) { if (whiteout) error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); else { error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); } } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); return (error); } /* * Create a named pipe. */ #ifndef _SYS_SYSPROTO_H_ struct mkfifo_args { char *path; int mode; }; #endif int sys_mkfifo(struct thread *td, struct mkfifo_args *uap) { return (kern_mkfifoat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->mode)); } #ifndef _SYS_SYSPROTO_H_ struct mkfifoat_args { int fd; char *path; mode_t mode; }; #endif int sys_mkfifoat(struct thread *td, struct mkfifoat_args *uap) { return (kern_mkfifoat(td, uap->fd, uap->path, UIO_USERSPACE, uap->mode)); } int kern_mkfifoat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, int mode) { struct mount *mp; struct vattr vattr; struct nameidata nd; int error; AUDIT_ARG_MODE(mode); restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1 | NOCACHE, pathseg, path, fd, &cap_mkfifoat_rights, td); if ((error = namei(&nd)) != 0) return (error); if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VFIFO; vattr.va_mode = (mode & ALLPERMS) & ~td->td_proc->p_fd->fd_cmask; #ifdef MAC error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error != 0) goto out; #endif error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); #ifdef MAC out: #endif vput(nd.ni_dvp); vn_finished_write(mp); NDFREE(&nd, NDF_ONLY_PNBUF); return (error); } /* * Make a hard file link. */ #ifndef _SYS_SYSPROTO_H_ struct link_args { char *path; char *link; }; #endif int sys_link(struct thread *td, struct link_args *uap) { return (kern_linkat(td, AT_FDCWD, AT_FDCWD, uap->path, uap->link, UIO_USERSPACE, FOLLOW)); } #ifndef _SYS_SYSPROTO_H_ struct linkat_args { int fd1; char *path1; int fd2; char *path2; int flag; }; #endif int sys_linkat(struct thread *td, struct linkat_args *uap) { int flag; flag = uap->flag; if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH)) != 0) return (EINVAL); return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2, UIO_USERSPACE, ((flag & AT_SYMLINK_FOLLOW) != 0 ? FOLLOW : NOFOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0))); } int hardlink_check_uid = 0; SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, &hardlink_check_uid, 0, "Unprivileged processes cannot create hard links to files owned by other " "users"); static int hardlink_check_gid = 0; SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_gid, CTLFLAG_RW, &hardlink_check_gid, 0, "Unprivileged processes cannot create hard links to files owned by other " "groups"); static int can_hardlink(struct vnode *vp, struct ucred *cred) { struct vattr va; int error; if (!hardlink_check_uid && !hardlink_check_gid) return (0); error = VOP_GETATTR(vp, &va, cred); if (error != 0) return (error); if (hardlink_check_uid && cred->cr_uid != va.va_uid) { error = priv_check_cred(cred, PRIV_VFS_LINK, 0); if (error != 0) return (error); } if (hardlink_check_gid && !groupmember(va.va_gid, cred)) { error = priv_check_cred(cred, PRIV_VFS_LINK, 0); if (error != 0) return (error); } return (0); } int kern_linkat(struct thread *td, int fd1, int fd2, const char *path1, const char *path2, enum uio_seg segflag, int follow) { struct nameidata nd; int error; do { bwillwrite(); NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, segflag, path1, fd1, &cap_linkat_source_rights, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = kern_linkat_vp(td, nd.ni_vp, fd2, path2, segflag); } while (error == EAGAIN); return (error); } static int kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path, enum uio_seg segflag) { struct nameidata nd; struct mount *mp; int error; if (vp->v_type == VDIR) { vrele(vp); return (EPERM); /* POSIX */ } NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflag, path, fd, &cap_linkat_target_rights, td); if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); vrele(vp); return (EEXIST); } else if (nd.ni_dvp->v_mount != vp->v_mount) { /* * Cross-device link. No need to recheck * vp->v_type, since it cannot change, except * to VBAD. */ NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vrele(vp); return (EXDEV); } else if ((error = vn_lock(vp, LK_EXCLUSIVE)) == 0) { error = can_hardlink(vp, td->td_ucred); #ifdef MAC if (error == 0) error = mac_vnode_check_link(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); #endif if (error != 0) { vput(vp); vput(nd.ni_dvp); NDFREE(&nd, NDF_ONLY_PNBUF); return (error); } error = vn_start_write(vp, &mp, V_NOWAIT); if (error != 0) { vput(vp); vput(nd.ni_dvp); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH); if (error != 0) return (error); return (EAGAIN); } error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); VOP_UNLOCK(vp, 0); vput(nd.ni_dvp); vn_finished_write(mp); NDFREE(&nd, NDF_ONLY_PNBUF); } else { vput(nd.ni_dvp); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); return (EAGAIN); } } vrele(vp); return (error); } /* * Make a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct symlink_args { char *path; char *link; }; #endif int sys_symlink(struct thread *td, struct symlink_args *uap) { return (kern_symlinkat(td, uap->path, AT_FDCWD, uap->link, UIO_USERSPACE)); } #ifndef _SYS_SYSPROTO_H_ struct symlinkat_args { char *path; int fd; char *path2; }; #endif int sys_symlinkat(struct thread *td, struct symlinkat_args *uap) { return (kern_symlinkat(td, uap->path1, uap->fd, uap->path2, UIO_USERSPACE)); } int kern_symlinkat(struct thread *td, const char *path1, int fd, const char *path2, enum uio_seg segflg) { struct mount *mp; struct vattr vattr; const char *syspath; char *tmppath; struct nameidata nd; int error; if (segflg == UIO_SYSSPACE) { syspath = path1; } else { tmppath = uma_zalloc(namei_zone, M_WAITOK); if ((error = copyinstr(path1, tmppath, MAXPATHLEN, NULL)) != 0) goto out; syspath = tmppath; } AUDIT_ARG_TEXT(syspath); restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1 | NOCACHE, segflg, path2, fd, &cap_symlinkat_rights, td); if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); error = EEXIST; goto out; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) goto out; goto restart; } VATTR_NULL(&vattr); vattr.va_mode = ACCESSPERMS &~ td->td_proc->p_fd->fd_cmask; #ifdef MAC vattr.va_type = VLNK; error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error != 0) goto out2; #endif error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, syspath); if (error == 0) vput(nd.ni_vp); #ifdef MAC out2: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); out: if (segflg != UIO_SYSSPACE) uma_zfree(namei_zone, tmppath); return (error); } /* * Delete a whiteout from the filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct undelete_args { char *path; }; #endif int sys_undelete(struct thread *td, struct undelete_args *uap) { struct mount *mp; struct nameidata nd; int error; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT | DOWHITEOUT | AUDITVNODE1, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error != 0) return (error); if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); return (error); } /* * Delete a name from the filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct unlink_args { char *path; }; #endif int sys_unlink(struct thread *td, struct unlink_args *uap) { return (kern_unlinkat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0, 0)); } #ifndef _SYS_SYSPROTO_H_ struct unlinkat_args { int fd; char *path; int flag; }; #endif int sys_unlinkat(struct thread *td, struct unlinkat_args *uap) { int fd, flag; const char *path; flag = uap->flag; fd = uap->fd; path = uap->path; if ((flag & ~(AT_REMOVEDIR | AT_BENEATH)) != 0) return (EINVAL); if ((uap->flag & AT_REMOVEDIR) != 0) return (kern_rmdirat(td, fd, path, UIO_USERSPACE, flag)); else return (kern_unlinkat(td, fd, path, UIO_USERSPACE, flag, 0)); } int kern_unlinkat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, int flag, ino_t oldinum) { struct mount *mp; struct vnode *vp; struct nameidata nd; struct stat sb; int error; restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | ((flag & AT_BENEATH) != 0 ? BENEATH : 0), pathseg, path, fd, &cap_unlinkat_rights, td); if ((error = namei(&nd)) != 0) return (error == EINVAL ? EPERM : error); vp = nd.ni_vp; if (vp->v_type == VDIR && oldinum == 0) { error = EPERM; /* POSIX */ } else if (oldinum != 0 && ((error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td)) == 0) && sb.st_ino != oldinum) { error = EIDRM; /* Identifier removed */ } else { /* * The root of a mounted filesystem cannot be deleted. * * XXX: can this only be a VDIR case? */ if (vp->v_vflag & VV_ROOT) error = EBUSY; } if (error == 0) { if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (vp == nd.ni_dvp) vrele(vp); else vput(vp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } #ifdef MAC error = mac_vnode_check_unlink(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); if (error != 0) goto out; #endif vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK); error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); #ifdef MAC out: #endif vn_finished_write(mp); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (vp == nd.ni_dvp) vrele(vp); else vput(vp); return (error); } /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct lseek_args { int fd; int pad; off_t offset; int whence; }; #endif int sys_lseek(struct thread *td, struct lseek_args *uap) { return (kern_lseek(td, uap->fd, uap->offset, uap->whence)); } int kern_lseek(struct thread *td, int fd, off_t offset, int whence) { struct file *fp; int error; AUDIT_ARG_FD(fd); error = fget(td, fd, &cap_seek_rights, &fp); if (error != 0) return (error); error = (fp->f_ops->fo_flags & DFLAG_SEEKABLE) != 0 ? fo_seek(fp, offset, whence, td) : ESPIPE; fdrop(fp, td); return (error); } #if defined(COMPAT_43) /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct olseek_args { int fd; long offset; int whence; }; #endif int olseek(struct thread *td, struct olseek_args *uap) { return (kern_lseek(td, uap->fd, uap->offset, uap->whence)); } #endif /* COMPAT_43 */ #if defined(COMPAT_FREEBSD6) /* Version with the 'pad' argument */ int freebsd6_lseek(struct thread *td, struct freebsd6_lseek_args *uap) { return (kern_lseek(td, uap->fd, uap->offset, uap->whence)); } #endif /* * Check access permissions using passed credentials. */ static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred, struct thread *td) { accmode_t accmode; int error; /* Flags == 0 means only check for existence. */ if (user_flags == 0) return (0); accmode = 0; if (user_flags & R_OK) accmode |= VREAD; if (user_flags & W_OK) accmode |= VWRITE; if (user_flags & X_OK) accmode |= VEXEC; #ifdef MAC error = mac_vnode_check_access(cred, vp, accmode); if (error != 0) return (error); #endif if ((accmode & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, accmode, cred, td); return (error); } /* * Check access permissions using "real" credentials. */ #ifndef _SYS_SYSPROTO_H_ struct access_args { char *path; int amode; }; #endif int sys_access(struct thread *td, struct access_args *uap) { return (kern_accessat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0, uap->amode)); } #ifndef _SYS_SYSPROTO_H_ struct faccessat_args { int dirfd; char *path; int amode; int flag; } #endif int sys_faccessat(struct thread *td, struct faccessat_args *uap) { return (kern_accessat(td, uap->fd, uap->path, UIO_USERSPACE, uap->flag, uap->amode)); } int kern_accessat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, int flag, int amode) { struct ucred *cred, *usecred; struct vnode *vp; struct nameidata nd; int error; if ((flag & ~(AT_EACCESS | AT_BENEATH)) != 0) return (EINVAL); if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0) return (EINVAL); /* * Create and modify a temporary credential instead of one that * is potentially shared (if we need one). */ cred = td->td_ucred; if ((flag & AT_EACCESS) == 0 && ((cred->cr_uid != cred->cr_ruid || cred->cr_rgid != cred->cr_groups[0]))) { usecred = crdup(cred); usecred->cr_uid = cred->cr_ruid; usecred->cr_groups[0] = cred->cr_rgid; td->td_ucred = usecred; } else usecred = cred; AUDIT_ARG_VALUE(amode); NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1 | ((flag & AT_BENEATH) != 0 ? BENEATH : 0), pathseg, path, fd, &cap_fstat_rights, td); if ((error = namei(&nd)) != 0) goto out; vp = nd.ni_vp; error = vn_access(vp, amode, usecred, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); out: if (usecred != cred) { td->td_ucred = cred; crfree(usecred); } return (error); } /* * Check access permissions using "effective" credentials. */ #ifndef _SYS_SYSPROTO_H_ struct eaccess_args { char *path; int amode; }; #endif int sys_eaccess(struct thread *td, struct eaccess_args *uap) { return (kern_accessat(td, AT_FDCWD, uap->path, UIO_USERSPACE, AT_EACCESS, uap->amode)); } #if defined(COMPAT_43) /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct ostat_args { char *path; struct ostat *ub; }; #endif int ostat(struct thread *td, struct ostat_args *uap) { struct stat sb; struct ostat osb; int error; error = kern_statat(td, 0, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); cvtstat(&sb, &osb); return (copyout(&osb, uap->ub, sizeof (osb))); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct olstat_args { char *path; struct ostat *ub; }; #endif int olstat(struct thread *td, struct olstat_args *uap) { struct stat sb; struct ostat osb; int error; error = kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); cvtstat(&sb, &osb); return (copyout(&osb, uap->ub, sizeof (osb))); } /* * Convert from an old to a new stat structure. * XXX: many values are blindly truncated. */ void cvtstat(struct stat *st, struct ostat *ost) { bzero(ost, sizeof(*ost)); ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; ost->st_size = MIN(st->st_size, INT32_MAX); ost->st_atim = st->st_atim; ost->st_mtim = st->st_mtim; ost->st_ctim = st->st_ctim; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } #endif /* COMPAT_43 */ #if defined(COMPAT_43) || defined(COMPAT_FREEBSD11) int ino64_trunc_error; SYSCTL_INT(_vfs, OID_AUTO, ino64_trunc_error, CTLFLAG_RW, &ino64_trunc_error, 0, "Error on truncation of device, file or inode number, or link count"); int freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost) { ost->st_dev = st->st_dev; if (ost->st_dev != st->st_dev) { switch (ino64_trunc_error) { default: /* * Since dev_t is almost raw, don't clamp to the * maximum for case 2, but ignore the error. */ break; case 1: return (EOVERFLOW); } } ost->st_ino = st->st_ino; if (ost->st_ino != st->st_ino) { switch (ino64_trunc_error) { default: case 0: break; case 1: return (EOVERFLOW); case 2: ost->st_ino = UINT32_MAX; break; } } ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; if (ost->st_nlink != st->st_nlink) { switch (ino64_trunc_error) { default: case 0: break; case 1: return (EOVERFLOW); case 2: ost->st_nlink = UINT16_MAX; break; } } ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (ost->st_rdev != st->st_rdev) { switch (ino64_trunc_error) { default: break; case 1: return (EOVERFLOW); } } ost->st_atim = st->st_atim; ost->st_mtim = st->st_mtim; ost->st_ctim = st->st_ctim; ost->st_size = st->st_size; ost->st_blocks = st->st_blocks; ost->st_blksize = st->st_blksize; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; ost->st_lspare = 0; ost->st_birthtim = st->st_birthtim; bzero((char *)&ost->st_birthtim + sizeof(ost->st_birthtim), sizeof(*ost) - offsetof(struct freebsd11_stat, st_birthtim) - sizeof(ost->st_birthtim)); return (0); } int freebsd11_stat(struct thread *td, struct freebsd11_stat_args* uap) { struct stat sb; struct freebsd11_stat osb; int error; error = kern_statat(td, 0, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); error = freebsd11_cvtstat(&sb, &osb); if (error == 0) error = copyout(&osb, uap->ub, sizeof(osb)); return (error); } int freebsd11_lstat(struct thread *td, struct freebsd11_lstat_args* uap) { struct stat sb; struct freebsd11_stat osb; int error; error = kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); error = freebsd11_cvtstat(&sb, &osb); if (error == 0) error = copyout(&osb, uap->ub, sizeof(osb)); return (error); } int freebsd11_fhstat(struct thread *td, struct freebsd11_fhstat_args* uap) { struct fhandle fh; struct stat sb; struct freebsd11_stat osb; int error; error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); if (error != 0) return (error); error = kern_fhstat(td, fh, &sb); if (error != 0) return (error); error = freebsd11_cvtstat(&sb, &osb); if (error == 0) error = copyout(&osb, uap->sb, sizeof(osb)); return (error); } int freebsd11_fstatat(struct thread *td, struct freebsd11_fstatat_args* uap) { struct stat sb; struct freebsd11_stat osb; int error; error = kern_statat(td, uap->flag, uap->fd, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); error = freebsd11_cvtstat(&sb, &osb); if (error == 0) error = copyout(&osb, uap->buf, sizeof(osb)); return (error); } #endif /* COMPAT_FREEBSD11 */ /* * Get file status */ #ifndef _SYS_SYSPROTO_H_ struct fstatat_args { int fd; char *path; struct stat *buf; int flag; } #endif int sys_fstatat(struct thread *td, struct fstatat_args *uap) { struct stat sb; int error; error = kern_statat(td, uap->flag, uap->fd, uap->path, UIO_USERSPACE, &sb, NULL); if (error == 0) error = copyout(&sb, uap->buf, sizeof (sb)); return (error); } int kern_statat(struct thread *td, int flag, int fd, const char *path, enum uio_seg pathseg, struct stat *sbp, void (*hook)(struct vnode *vp, struct stat *sbp)) { struct nameidata nd; int error; if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : FOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0) | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, td); if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, sbp, td->td_ucred, NOCRED, td); if (error == 0) { SDT_PROBE2(vfs, , stat, mode, path, sbp->st_mode); if (S_ISREG(sbp->st_mode)) SDT_PROBE2(vfs, , stat, reg, path, pathseg); if (__predict_false(hook != NULL)) hook(nd.ni_vp, sbp); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); if (error != 0) return (error); #ifdef __STAT_TIME_T_EXT sbp->st_atim_ext = 0; sbp->st_mtim_ext = 0; sbp->st_ctim_ext = 0; sbp->st_btim_ext = 0; #endif #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrstat(sbp); #endif return (0); } #if defined(COMPAT_FREEBSD11) /* * Implementation of the NetBSD [l]stat() functions. */ void freebsd11_cvtnstat(struct stat *sb, struct nstat *nsb) { bzero(nsb, sizeof(*nsb)); nsb->st_dev = sb->st_dev; nsb->st_ino = sb->st_ino; nsb->st_mode = sb->st_mode; nsb->st_nlink = sb->st_nlink; nsb->st_uid = sb->st_uid; nsb->st_gid = sb->st_gid; nsb->st_rdev = sb->st_rdev; nsb->st_atim = sb->st_atim; nsb->st_mtim = sb->st_mtim; nsb->st_ctim = sb->st_ctim; nsb->st_size = sb->st_size; nsb->st_blocks = sb->st_blocks; nsb->st_blksize = sb->st_blksize; nsb->st_flags = sb->st_flags; nsb->st_gen = sb->st_gen; nsb->st_birthtim = sb->st_birthtim; } #ifndef _SYS_SYSPROTO_H_ struct freebsd11_nstat_args { char *path; struct nstat *ub; }; #endif int freebsd11_nstat(struct thread *td, struct freebsd11_nstat_args *uap) { struct stat sb; struct nstat nsb; int error; error = kern_statat(td, 0, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); freebsd11_cvtnstat(&sb, &nsb); return (copyout(&nsb, uap->ub, sizeof (nsb))); } /* * NetBSD lstat. Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct freebsd11_nlstat_args { char *path; struct nstat *ub; }; #endif int freebsd11_nlstat(struct thread *td, struct freebsd11_nlstat_args *uap) { struct stat sb; struct nstat nsb; int error; error = kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->path, UIO_USERSPACE, &sb, NULL); if (error != 0) return (error); freebsd11_cvtnstat(&sb, &nsb); return (copyout(&nsb, uap->ub, sizeof (nsb))); } #endif /* COMPAT_FREEBSD11 */ /* * Get configurable pathname variables. */ #ifndef _SYS_SYSPROTO_H_ struct pathconf_args { char *path; int name; }; #endif int sys_pathconf(struct thread *td, struct pathconf_args *uap) { long value; int error; error = kern_pathconf(td, uap->path, UIO_USERSPACE, uap->name, FOLLOW, &value); if (error == 0) td->td_retval[0] = value; return (error); } #ifndef _SYS_SYSPROTO_H_ struct lpathconf_args { char *path; int name; }; #endif int sys_lpathconf(struct thread *td, struct lpathconf_args *uap) { long value; int error; error = kern_pathconf(td, uap->path, UIO_USERSPACE, uap->name, NOFOLLOW, &value); if (error == 0) td->td_retval[0] = value; return (error); } int kern_pathconf(struct thread *td, const char *path, enum uio_seg pathseg, int name, u_long flags, long *valuep) { struct nameidata nd; int error; NDINIT(&nd, LOOKUP, LOCKSHARED | LOCKLEAF | AUDITVNODE1 | flags, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = VOP_PATHCONF(nd.ni_vp, name, valuep); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct readlink_args { char *path; char *buf; size_t count; }; #endif int sys_readlink(struct thread *td, struct readlink_args *uap) { return (kern_readlinkat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->buf, UIO_USERSPACE, uap->count)); } #ifndef _SYS_SYSPROTO_H_ struct readlinkat_args { int fd; char *path; char *buf; size_t bufsize; }; #endif int sys_readlinkat(struct thread *td, struct readlinkat_args *uap) { return (kern_readlinkat(td, uap->fd, uap->path, UIO_USERSPACE, uap->buf, UIO_USERSPACE, uap->bufsize)); } int kern_readlinkat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, char *buf, enum uio_seg bufseg, size_t count) { struct vnode *vp; struct nameidata nd; int error; if (count > IOSIZE_MAX) return (EINVAL); NDINIT_AT(&nd, LOOKUP, NOFOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, fd, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; error = kern_readlink_vp(vp, buf, bufseg, count, td); vput(vp); return (error); } /* * Helper function to readlink from a vnode */ static int kern_readlink_vp(struct vnode *vp, char *buf, enum uio_seg bufseg, size_t count, struct thread *td) { struct iovec aiov; struct uio auio; int error; ASSERT_VOP_LOCKED(vp, "kern_readlink_vp(): vp not locked"); #ifdef MAC error = mac_vnode_check_readlink(td->td_ucred, vp); if (error != 0) return (error); #endif if (vp->v_type != VLNK && (vp->v_vflag & VV_READLINK) == 0) return (EINVAL); aiov.iov_base = buf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = bufseg; auio.uio_td = td; auio.uio_resid = count; error = VOP_READLINK(vp, &auio, td->td_ucred); td->td_retval[0] = count - auio.uio_resid; return (error); } /* * Common implementation code for chflags() and fchflags(). */ static int setfflags(struct thread *td, struct vnode *vp, u_long flags) { struct mount *mp; struct vattr vattr; int error; /* We can't support the value matching VNOVAL. */ if (flags == VNOVAL) return (EOPNOTSUPP); /* * Prevent non-root users from setting flags on devices. When * a device is reused, users can retain ownership of the device * if they are allowed to set flags and programs assume that * chown can't fail when done as root. */ if (vp->v_type == VCHR || vp->v_type == VBLK) { error = priv_check(td, PRIV_VFS_CHFLAGS_DEV); if (error != 0) return (error); } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VATTR_NULL(&vattr); vattr.va_flags = flags; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); #ifdef MAC error = mac_vnode_check_setflags(td->td_ucred, vp, vattr.va_flags); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, td->td_ucred); VOP_UNLOCK(vp, 0); vn_finished_write(mp); return (error); } /* * Change flags of a file given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chflags_args { const char *path; u_long flags; }; #endif int sys_chflags(struct thread *td, struct chflags_args *uap) { return (kern_chflagsat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->flags, 0)); } #ifndef _SYS_SYSPROTO_H_ struct chflagsat_args { int fd; const char *path; u_long flags; int atflag; } #endif int sys_chflagsat(struct thread *td, struct chflagsat_args *uap) { if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE, uap->flags, uap->atflag)); } /* * Same as chflags() but doesn't follow symlinks. */ #ifndef _SYS_SYSPROTO_H_ struct lchflags_args { const char *path; u_long flags; }; #endif int sys_lchflags(struct thread *td, struct lchflags_args *uap) { return (kern_chflagsat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->flags, AT_SYMLINK_NOFOLLOW)); } static int kern_chflagsat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, u_long flags, int atflag) { struct nameidata nd; int error, follow; AUDIT_ARG_FFLAGS(flags); follow = (atflag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; follow |= (atflag & AT_BENEATH) != 0 ? BENEATH : 0; NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, &cap_fchflags_rights, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(td, nd.ni_vp, flags); vrele(nd.ni_vp); return (error); } /* * Change flags of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchflags_args { int fd; u_long flags; }; #endif int sys_fchflags(struct thread *td, struct fchflags_args *uap) { struct file *fp; int error; AUDIT_ARG_FD(uap->fd); AUDIT_ARG_FFLAGS(uap->flags); error = getvnode(td, uap->fd, &cap_fchflags_rights, &fp); if (error != 0) return (error); #ifdef AUDIT vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY); AUDIT_ARG_VNODE1(fp->f_vnode); VOP_UNLOCK(fp->f_vnode, 0); #endif error = setfflags(td, fp->f_vnode, uap->flags); fdrop(fp, td); return (error); } /* * Common implementation code for chmod(), lchmod() and fchmod(). */ int setfmode(struct thread *td, struct ucred *cred, struct vnode *vp, int mode) { struct mount *mp; struct vattr vattr; int error; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); VATTR_NULL(&vattr); vattr.va_mode = mode & ALLPERMS; #ifdef MAC error = mac_vnode_check_setmode(cred, vp, vattr.va_mode); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, cred); VOP_UNLOCK(vp, 0); vn_finished_write(mp); return (error); } /* * Change mode of a file given path name. */ #ifndef _SYS_SYSPROTO_H_ struct chmod_args { char *path; int mode; }; #endif int sys_chmod(struct thread *td, struct chmod_args *uap) { return (kern_fchmodat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->mode, 0)); } #ifndef _SYS_SYSPROTO_H_ struct fchmodat_args { int dirfd; char *path; mode_t mode; int flag; } #endif int sys_fchmodat(struct thread *td, struct fchmodat_args *uap) { if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE, uap->mode, uap->flag)); } /* * Change mode of a file given path name (don't follow links.) */ #ifndef _SYS_SYSPROTO_H_ struct lchmod_args { char *path; int mode; }; #endif int sys_lchmod(struct thread *td, struct lchmod_args *uap) { return (kern_fchmodat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->mode, AT_SYMLINK_NOFOLLOW)); } int kern_fchmodat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, mode_t mode, int flag) { struct nameidata nd; int error, follow; AUDIT_ARG_MODE(mode); follow = (flag & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : FOLLOW; follow |= (flag & AT_BENEATH) != 0 ? BENEATH : 0; NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, &cap_fchmod_rights, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(td, td->td_ucred, nd.ni_vp, mode); vrele(nd.ni_vp); return (error); } /* * Change mode of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchmod_args { int fd; int mode; }; #endif int sys_fchmod(struct thread *td, struct fchmod_args *uap) { struct file *fp; int error; AUDIT_ARG_FD(uap->fd); AUDIT_ARG_MODE(uap->mode); error = fget(td, uap->fd, &cap_fchmod_rights, &fp); if (error != 0) return (error); error = fo_chmod(fp, uap->mode, td->td_ucred, td); fdrop(fp, td); return (error); } /* * Common implementation for chown(), lchown(), and fchown() */ int setfown(struct thread *td, struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { struct mount *mp; struct vattr vattr; int error; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); VATTR_NULL(&vattr); vattr.va_uid = uid; vattr.va_gid = gid; #ifdef MAC error = mac_vnode_check_setowner(cred, vp, vattr.va_uid, vattr.va_gid); if (error == 0) #endif error = VOP_SETATTR(vp, &vattr, cred); VOP_UNLOCK(vp, 0); vn_finished_write(mp); return (error); } /* * Set ownership given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chown_args { char *path; int uid; int gid; }; #endif int sys_chown(struct thread *td, struct chown_args *uap) { return (kern_fchownat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->uid, uap->gid, 0)); } #ifndef _SYS_SYSPROTO_H_ struct fchownat_args { int fd; const char * path; uid_t uid; gid_t gid; int flag; }; #endif int sys_fchownat(struct thread *td, struct fchownat_args *uap) { if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid, uap->gid, uap->flag)); } int kern_fchownat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, int uid, int gid, int flag) { struct nameidata nd; int error, follow; AUDIT_ARG_OWNER(uid, gid); follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; follow |= (flag & AT_BENEATH) != 0 ? BENEATH : 0; NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, &cap_fchown_rights, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(td, td->td_ucred, nd.ni_vp, uid, gid); vrele(nd.ni_vp); return (error); } /* * Set ownership given a path name, do not cross symlinks. */ #ifndef _SYS_SYSPROTO_H_ struct lchown_args { char *path; int uid; int gid; }; #endif int sys_lchown(struct thread *td, struct lchown_args *uap) { return (kern_fchownat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->uid, uap->gid, AT_SYMLINK_NOFOLLOW)); } /* * Set ownership given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchown_args { int fd; int uid; int gid; }; #endif int sys_fchown(struct thread *td, struct fchown_args *uap) { struct file *fp; int error; AUDIT_ARG_FD(uap->fd); AUDIT_ARG_OWNER(uap->uid, uap->gid); error = fget(td, uap->fd, &cap_fchown_rights, &fp); if (error != 0) return (error); error = fo_chown(fp, uap->uid, uap->gid, td->td_ucred, td); fdrop(fp, td); return (error); } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int getutimes(const struct timeval *usrtvp, enum uio_seg tvpseg, struct timespec *tsp) { struct timeval tv[2]; const struct timeval *tvp; int error; if (usrtvp == NULL) { vfs_timestamp(&tsp[0]); tsp[1] = tsp[0]; } else { if (tvpseg == UIO_SYSSPACE) { tvp = usrtvp; } else { if ((error = copyin(usrtvp, tv, sizeof(tv))) != 0) return (error); tvp = tv; } if (tvp[0].tv_usec < 0 || tvp[0].tv_usec >= 1000000 || tvp[1].tv_usec < 0 || tvp[1].tv_usec >= 1000000) return (EINVAL); TIMEVAL_TO_TIMESPEC(&tvp[0], &tsp[0]); TIMEVAL_TO_TIMESPEC(&tvp[1], &tsp[1]); } return (0); } /* * Common implementation code for futimens(), utimensat(). */ #define UTIMENS_NULL 0x1 #define UTIMENS_EXIT 0x2 static int getutimens(const struct timespec *usrtsp, enum uio_seg tspseg, struct timespec *tsp, int *retflags) { struct timespec tsnow; int error; vfs_timestamp(&tsnow); *retflags = 0; if (usrtsp == NULL) { tsp[0] = tsnow; tsp[1] = tsnow; *retflags |= UTIMENS_NULL; return (0); } if (tspseg == UIO_SYSSPACE) { tsp[0] = usrtsp[0]; tsp[1] = usrtsp[1]; } else if ((error = copyin(usrtsp, tsp, sizeof(*tsp) * 2)) != 0) return (error); if (tsp[0].tv_nsec == UTIME_OMIT && tsp[1].tv_nsec == UTIME_OMIT) *retflags |= UTIMENS_EXIT; if (tsp[0].tv_nsec == UTIME_NOW && tsp[1].tv_nsec == UTIME_NOW) *retflags |= UTIMENS_NULL; if (tsp[0].tv_nsec == UTIME_OMIT) tsp[0].tv_sec = VNOVAL; else if (tsp[0].tv_nsec == UTIME_NOW) tsp[0] = tsnow; else if (tsp[0].tv_nsec < 0 || tsp[0].tv_nsec >= 1000000000L) return (EINVAL); if (tsp[1].tv_nsec == UTIME_OMIT) tsp[1].tv_sec = VNOVAL; else if (tsp[1].tv_nsec == UTIME_NOW) tsp[1] = tsnow; else if (tsp[1].tv_nsec < 0 || tsp[1].tv_nsec >= 1000000000L) return (EINVAL); return (0); } /* * Common implementation code for utimes(), lutimes(), futimes(), futimens(), * and utimensat(). */ static int setutimes(struct thread *td, struct vnode *vp, const struct timespec *ts, int numtimes, int nullflag) { struct mount *mp; struct vattr vattr; int error, setbirthtime; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); setbirthtime = 0; if (numtimes < 3 && !VOP_GETATTR(vp, &vattr, td->td_ucred) && timespeccmp(&ts[1], &vattr.va_birthtime, < )) setbirthtime = 1; VATTR_NULL(&vattr); vattr.va_atime = ts[0]; vattr.va_mtime = ts[1]; if (setbirthtime) vattr.va_birthtime = ts[1]; if (numtimes > 2) vattr.va_birthtime = ts[2]; if (nullflag) vattr.va_vaflags |= VA_UTIMES_NULL; #ifdef MAC error = mac_vnode_check_setutimes(td->td_ucred, vp, vattr.va_atime, vattr.va_mtime); #endif if (error == 0) error = VOP_SETATTR(vp, &vattr, td->td_ucred); VOP_UNLOCK(vp, 0); vn_finished_write(mp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct utimes_args { char *path; struct timeval *tptr; }; #endif int sys_utimes(struct thread *td, struct utimes_args *uap) { return (kern_utimesat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->tptr, UIO_USERSPACE)); } #ifndef _SYS_SYSPROTO_H_ struct futimesat_args { int fd; const char * path; const struct timeval * times; }; #endif int sys_futimesat(struct thread *td, struct futimesat_args *uap) { return (kern_utimesat(td, uap->fd, uap->path, UIO_USERSPACE, uap->times, UIO_USERSPACE)); } int kern_utimesat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg) { struct nameidata nd; struct timespec ts[2]; int error; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | AUDITVNODE1, pathseg, path, fd, &cap_futimes_rights, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(td, nd.ni_vp, ts, 2, tptr == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct lutimes_args { char *path; struct timeval *tptr; }; #endif int sys_lutimes(struct thread *td, struct lutimes_args *uap) { return (kern_lutimes(td, uap->path, UIO_USERSPACE, uap->tptr, UIO_USERSPACE)); } int kern_lutimes(struct thread *td, const char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; struct nameidata nd; int error; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(td, nd.ni_vp, ts, 2, tptr == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct futimes_args { int fd; struct timeval *tptr; }; #endif int sys_futimes(struct thread *td, struct futimes_args *uap) { return (kern_futimes(td, uap->fd, uap->tptr, UIO_USERSPACE)); } int kern_futimes(struct thread *td, int fd, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; struct file *fp; int error; AUDIT_ARG_FD(fd); error = getutimes(tptr, tptrseg, ts); if (error != 0) return (error); error = getvnode(td, fd, &cap_futimes_rights, &fp); if (error != 0) return (error); #ifdef AUDIT vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY); AUDIT_ARG_VNODE1(fp->f_vnode); VOP_UNLOCK(fp->f_vnode, 0); #endif error = setutimes(td, fp->f_vnode, ts, 2, tptr == NULL); fdrop(fp, td); return (error); } int sys_futimens(struct thread *td, struct futimens_args *uap) { return (kern_futimens(td, uap->fd, uap->times, UIO_USERSPACE)); } int kern_futimens(struct thread *td, int fd, struct timespec *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; struct file *fp; int error, flags; AUDIT_ARG_FD(fd); error = getutimens(tptr, tptrseg, ts, &flags); if (error != 0) return (error); if (flags & UTIMENS_EXIT) return (0); error = getvnode(td, fd, &cap_futimes_rights, &fp); if (error != 0) return (error); #ifdef AUDIT vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY); AUDIT_ARG_VNODE1(fp->f_vnode); VOP_UNLOCK(fp->f_vnode, 0); #endif error = setutimes(td, fp->f_vnode, ts, 2, flags & UTIMENS_NULL); fdrop(fp, td); return (error); } int sys_utimensat(struct thread *td, struct utimensat_args *uap) { return (kern_utimensat(td, uap->fd, uap->path, UIO_USERSPACE, uap->times, UIO_USERSPACE, uap->flag)); } int kern_utimensat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, struct timespec *tptr, enum uio_seg tptrseg, int flag) { struct nameidata nd; struct timespec ts[2]; int error, flags; if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0) return (error); NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0) | AUDITVNODE1, pathseg, path, fd, &cap_futimes_rights, td); if ((error = namei(&nd)) != 0) return (error); /* * We are allowed to call namei() regardless of 2xUTIME_OMIT. * POSIX states: * "If both tv_nsec fields are UTIME_OMIT... EACCESS may be detected." * "Search permission is denied by a component of the path prefix." */ NDFREE(&nd, NDF_ONLY_PNBUF); if ((flags & UTIMENS_EXIT) == 0) error = setutimes(td, nd.ni_vp, ts, 2, flags & UTIMENS_NULL); vrele(nd.ni_vp); return (error); } /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct truncate_args { char *path; int pad; off_t length; }; #endif int sys_truncate(struct thread *td, struct truncate_args *uap) { return (kern_truncate(td, uap->path, UIO_USERSPACE, uap->length)); } int kern_truncate(struct thread *td, const char *path, enum uio_seg pathseg, off_t length) { struct mount *mp; struct vnode *vp; void *rl_cookie; struct vattr vattr; struct nameidata nd; int error; if (length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, pathseg, path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; rl_cookie = vn_rangelock_wlock(vp, 0, OFF_MAX); if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vn_rangelock_unlock(vp, rl_cookie); vrele(vp); return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); if (vp->v_type == VDIR) error = EISDIR; #ifdef MAC else if ((error = mac_vnode_check_write(td->td_ucred, NOCRED, vp))) { } #endif else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td)) == 0) { VATTR_NULL(&vattr); vattr.va_size = length; error = VOP_SETATTR(vp, &vattr, td->td_ucred); } VOP_UNLOCK(vp, 0); vn_finished_write(mp); vn_rangelock_unlock(vp, rl_cookie); vrele(vp); return (error); } #if defined(COMPAT_43) /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct otruncate_args { char *path; long length; }; #endif int otruncate(struct thread *td, struct otruncate_args *uap) { return (kern_truncate(td, uap->path, UIO_USERSPACE, uap->length)); } #endif /* COMPAT_43 */ #if defined(COMPAT_FREEBSD6) /* Versions with the pad argument */ int freebsd6_truncate(struct thread *td, struct freebsd6_truncate_args *uap) { return (kern_truncate(td, uap->path, UIO_USERSPACE, uap->length)); } int freebsd6_ftruncate(struct thread *td, struct freebsd6_ftruncate_args *uap) { return (kern_ftruncate(td, uap->fd, uap->length)); } #endif int kern_fsync(struct thread *td, int fd, bool fullsync) { struct vnode *vp; struct mount *mp; struct file *fp; int error, lock_flags; AUDIT_ARG_FD(fd); error = getvnode(td, fd, &cap_fsync_rights, &fp); if (error != 0) return (error); vp = fp->f_vnode; #if 0 if (!fullsync) /* XXXKIB: compete outstanding aio writes */; #endif error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) goto drop; if (MNT_SHARED_WRITES(mp) || ((mp == NULL) && MNT_SHARED_WRITES(vp->v_mount))) { lock_flags = LK_SHARED; } else { lock_flags = LK_EXCLUSIVE; } vn_lock(vp, lock_flags | LK_RETRY); AUDIT_ARG_VNODE1(vp); if (vp->v_object != NULL) { VM_OBJECT_WLOCK(vp->v_object); vm_object_page_clean(vp->v_object, 0, 0, 0); VM_OBJECT_WUNLOCK(vp->v_object); } error = fullsync ? VOP_FSYNC(vp, MNT_WAIT, td) : VOP_FDATASYNC(vp, td); VOP_UNLOCK(vp, 0); vn_finished_write(mp); drop: fdrop(fp, td); return (error); } /* * Sync an open file. */ #ifndef _SYS_SYSPROTO_H_ struct fsync_args { int fd; }; #endif int sys_fsync(struct thread *td, struct fsync_args *uap) { return (kern_fsync(td, uap->fd, true)); } int sys_fdatasync(struct thread *td, struct fdatasync_args *uap) { return (kern_fsync(td, uap->fd, false)); } /* * Rename files. Source and destination must either both be directories, or * both not be directories. If target is a directory, it must be empty. */ #ifndef _SYS_SYSPROTO_H_ struct rename_args { char *from; char *to; }; #endif int sys_rename(struct thread *td, struct rename_args *uap) { return (kern_renameat(td, AT_FDCWD, uap->from, AT_FDCWD, uap->to, UIO_USERSPACE)); } #ifndef _SYS_SYSPROTO_H_ struct renameat_args { int oldfd; char *old; int newfd; char *new; }; #endif int sys_renameat(struct thread *td, struct renameat_args *uap) { return (kern_renameat(td, uap->oldfd, uap->old, uap->newfd, uap->new, UIO_USERSPACE)); } int kern_renameat(struct thread *td, int oldfd, const char *old, int newfd, const char *new, enum uio_seg pathseg) { struct mount *mp = NULL; struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; again: bwillwrite(); #ifdef MAC NDINIT_ATRIGHTS(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART | AUDITVNODE1, pathseg, old, oldfd, &cap_renameat_source_rights, td); #else NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | SAVESTART | AUDITVNODE1, pathseg, old, oldfd, &cap_renameat_source_rights, td); #endif if ((error = namei(&fromnd)) != 0) return (error); #ifdef MAC error = mac_vnode_check_rename_from(td->td_ucred, fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd); VOP_UNLOCK(fromnd.ni_dvp, 0); if (fromnd.ni_dvp != fromnd.ni_vp) VOP_UNLOCK(fromnd.ni_vp, 0); #endif fvp = fromnd.ni_vp; NDINIT_ATRIGHTS(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | AUDITVNODE2, pathseg, new, newfd, &cap_renameat_target_rights, td); if (fromnd.ni_vp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&tond)) != 0) { /* Translate error code for rename("dir1", "dir2/."). */ if (error == EISDIR && fvp->v_type == VDIR) error = EINVAL; NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; error = vn_start_write(fvp, &mp, V_NOWAIT); if (error != 0) { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); if (tvp != NULL) vput(tvp); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); vrele(fromnd.ni_dvp); vrele(fvp); vrele(tond.ni_startdir); if (fromnd.ni_startdir != NULL) vrele(fromnd.ni_startdir); error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH); if (error != 0) return (error); goto again; } if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } #ifdef CAPABILITIES if (newfd != AT_FDCWD) { /* * If the target already exists we require CAP_UNLINKAT * from 'newfd'. */ error = cap_check(&tond.ni_filecaps.fc_rights, &cap_unlinkat_rights); if (error != 0) goto out; } #endif } if (fvp == tdvp) { error = EINVAL; goto out; } /* * If the source is the same as the destination (that is, if they * are links to the same vnode), then there is nothing to do. */ if (fvp == tvp) error = -1; #ifdef MAC else error = mac_vnode_check_rename_to(td->td_ucred, tdvp, tond.ni_vp, fromnd.ni_dvp == tdvp, &tond.ni_cnd); #endif out: if (error == 0) { error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); } else { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); if (tvp != NULL) vput(tvp); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); vn_finished_write(mp); out1: if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); if (error == -1) return (0); return (error); } /* * Make a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct mkdir_args { char *path; int mode; }; #endif int sys_mkdir(struct thread *td, struct mkdir_args *uap) { return (kern_mkdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE, uap->mode)); } #ifndef _SYS_SYSPROTO_H_ struct mkdirat_args { int fd; char *path; mode_t mode; }; #endif int sys_mkdirat(struct thread *td, struct mkdirat_args *uap) { return (kern_mkdirat(td, uap->fd, uap->path, UIO_USERSPACE, uap->mode)); } int kern_mkdirat(struct thread *td, int fd, const char *path, enum uio_seg segflg, int mode) { struct mount *mp; struct vnode *vp; struct vattr vattr; struct nameidata nd; int error; AUDIT_ARG_MODE(mode); restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1 | NOCACHE, segflg, path, fd, &cap_mkdirat_rights, td); nd.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); /* * XXX namei called with LOCKPARENT but not LOCKLEAF has * the strange behaviour of leaving the vnode unlocked * if the target is the same vnode as the parent. */ if (vp == nd.ni_dvp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(vp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VDIR; vattr.va_mode = (mode & ACCESSPERMS) &~ td->td_proc->p_fd->fd_cmask; #ifdef MAC error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); if (error != 0) goto out; #endif error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); #ifdef MAC out: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (error == 0) vput(nd.ni_vp); vn_finished_write(mp); return (error); } /* * Remove a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct rmdir_args { char *path; }; #endif int sys_rmdir(struct thread *td, struct rmdir_args *uap) { return (kern_rmdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0)); } int kern_rmdirat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, int flag) { struct mount *mp; struct vnode *vp; struct nameidata nd; int error; restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | ((flag & AT_BENEATH) != 0 ? BENEATH : 0), pathseg, path, fd, &cap_unlinkat_rights, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_vflag & VV_ROOT) { error = EBUSY; goto out; } #ifdef MAC error = mac_vnode_check_unlink(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); if (error != 0) goto out; #endif if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); vn_finished_write(mp); out: NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); return (error); } #if defined(COMPAT_43) || defined(COMPAT_FREEBSD11) int freebsd11_kern_getdirentries(struct thread *td, int fd, char *ubuf, u_int count, long *basep, void (*func)(struct freebsd11_dirent *)) { struct freebsd11_dirent dstdp; struct dirent *dp, *edp; char *dirbuf; off_t base; ssize_t resid, ucount; int error; /* XXX arbitrary sanity limit on `count'. */ count = min(count, 64 * 1024); dirbuf = malloc(count, M_TEMP, M_WAITOK); error = kern_getdirentries(td, fd, dirbuf, count, &base, &resid, UIO_SYSSPACE); if (error != 0) goto done; if (basep != NULL) *basep = base; ucount = 0; for (dp = (struct dirent *)dirbuf, edp = (struct dirent *)&dirbuf[count - resid]; ucount < count && dp < edp; ) { if (dp->d_reclen == 0) break; MPASS(dp->d_reclen >= _GENERIC_DIRLEN(0)); if (dp->d_namlen >= sizeof(dstdp.d_name)) continue; dstdp.d_type = dp->d_type; dstdp.d_namlen = dp->d_namlen; dstdp.d_fileno = dp->d_fileno; /* truncate */ if (dstdp.d_fileno != dp->d_fileno) { switch (ino64_trunc_error) { default: case 0: break; case 1: error = EOVERFLOW; goto done; case 2: dstdp.d_fileno = UINT32_MAX; break; } } dstdp.d_reclen = sizeof(dstdp) - sizeof(dstdp.d_name) + ((dp->d_namlen + 1 + 3) &~ 3); bcopy(dp->d_name, dstdp.d_name, dstdp.d_namlen); bzero(dstdp.d_name + dstdp.d_namlen, dstdp.d_reclen - offsetof(struct freebsd11_dirent, d_name) - dstdp.d_namlen); MPASS(dstdp.d_reclen <= dp->d_reclen); MPASS(ucount + dstdp.d_reclen <= count); if (func != NULL) func(&dstdp); error = copyout(&dstdp, ubuf + ucount, dstdp.d_reclen); if (error != 0) break; dp = (struct dirent *)((char *)dp + dp->d_reclen); ucount += dstdp.d_reclen; } done: free(dirbuf, M_TEMP); if (error == 0) td->td_retval[0] = ucount; return (error); } #endif /* COMPAT */ #ifdef COMPAT_43 static void ogetdirentries_cvt(struct freebsd11_dirent *dp) { #if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; #else /* * The dp->d_type is the high byte of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; #endif } /* * Read a block of directory entries in a filesystem independent format. */ #ifndef _SYS_SYSPROTO_H_ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int ogetdirentries(struct thread *td, struct ogetdirentries_args *uap) { long loff; int error; error = kern_ogetdirentries(td, uap, &loff); if (error == 0) error = copyout(&loff, uap->basep, sizeof(long)); return (error); } int kern_ogetdirentries(struct thread *td, struct ogetdirentries_args *uap, long *ploff) { long base; int error; /* XXX arbitrary sanity limit on `count'. */ if (uap->count > 64 * 1024) return (EINVAL); error = freebsd11_kern_getdirentries(td, uap->fd, uap->buf, uap->count, &base, ogetdirentries_cvt); if (error == 0 && uap->basep != NULL) error = copyout(&base, uap->basep, sizeof(long)); return (error); } #endif /* COMPAT_43 */ #if defined(COMPAT_FREEBSD11) #ifndef _SYS_SYSPROTO_H_ struct freebsd11_getdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int freebsd11_getdirentries(struct thread *td, struct freebsd11_getdirentries_args *uap) { long base; int error; error = freebsd11_kern_getdirentries(td, uap->fd, uap->buf, uap->count, &base, NULL); if (error == 0 && uap->basep != NULL) error = copyout(&base, uap->basep, sizeof(long)); return (error); } int freebsd11_getdents(struct thread *td, struct freebsd11_getdents_args *uap) { struct freebsd11_getdirentries_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; ap.basep = NULL; return (freebsd11_getdirentries(td, &ap)); } #endif /* COMPAT_FREEBSD11 */ /* * Read a block of directory entries in a filesystem independent format. */ int sys_getdirentries(struct thread *td, struct getdirentries_args *uap) { off_t base; int error; error = kern_getdirentries(td, uap->fd, uap->buf, uap->count, &base, NULL, UIO_USERSPACE); if (error != 0) return (error); if (uap->basep != NULL) error = copyout(&base, uap->basep, sizeof(off_t)); return (error); } int kern_getdirentries(struct thread *td, int fd, char *buf, size_t count, off_t *basep, ssize_t *residp, enum uio_seg bufseg) { struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; off_t loff; int error, eofflag; off_t foffset; AUDIT_ARG_FD(fd); if (count > IOSIZE_MAX) return (EINVAL); auio.uio_resid = count; error = getvnode(td, fd, &cap_read_rights, &fp); if (error != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); return (EBADF); } vp = fp->f_vnode; foffset = foffset_lock(fp, 0); unionread: if (vp->v_type != VDIR) { error = EINVAL; goto fail; } aiov.iov_base = buf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = bufseg; auio.uio_td = td; vn_lock(vp, LK_SHARED | LK_RETRY); AUDIT_ARG_VNODE1(vp); loff = auio.uio_offset = foffset; #ifdef MAC error = mac_vnode_check_readdir(td->td_ucred, vp); if (error == 0) #endif error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); foffset = auio.uio_offset; if (error != 0) { VOP_UNLOCK(vp, 0); goto fail; } if (count == auio.uio_resid && (vp->v_vflag & VV_ROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_vnode = vp; fp->f_data = vp; foffset = 0; vput(tvp); goto unionread; } VOP_UNLOCK(vp, 0); *basep = loff; if (residp != NULL) *residp = auio.uio_resid; td->td_retval[0] = count - auio.uio_resid; fail: foffset_unlock(fp, foffset, 0); fdrop(fp, td); return (error); } /* * Set the mode mask for creation of filesystem nodes. */ #ifndef _SYS_SYSPROTO_H_ struct umask_args { int newmask; }; #endif int sys_umask(struct thread *td, struct umask_args *uap) { struct filedesc *fdp; fdp = td->td_proc->p_fd; FILEDESC_XLOCK(fdp); td->td_retval[0] = fdp->fd_cmask; fdp->fd_cmask = uap->newmask & ALLPERMS; FILEDESC_XUNLOCK(fdp); return (0); } /* * Void all references to file by ripping underlying filesystem away from * vnode. */ #ifndef _SYS_SYSPROTO_H_ struct revoke_args { char *path; }; #endif int sys_revoke(struct thread *td, struct revoke_args *uap) { struct vnode *vp; struct vattr vattr; struct nameidata nd; int error; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VCHR || vp->v_rdev == NULL) { error = EINVAL; goto out; } #ifdef MAC error = mac_vnode_check_revoke(td->td_ucred, vp); if (error != 0) goto out; #endif error = VOP_GETATTR(vp, &vattr, td->td_ucred); if (error != 0) goto out; if (td->td_ucred->cr_uid != vattr.va_uid) { error = priv_check(td, PRIV_VFS_ADMIN); if (error != 0) goto out; } if (vcount(vp) > 1) VOP_REVOKE(vp, REVOKEALL); out: vput(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry and check that, if it * is a capability, the correct rights are present. A reference on the file * entry is held upon returning. */ int getvnode(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) { struct file *fp; int error; error = fget_unlocked(td->td_proc->p_fd, fd, rightsp, &fp, NULL); if (error != 0) return (error); /* * The file could be not of the vnode type, or it may be not * yet fully initialized, in which case the f_vnode pointer * may be set, but f_ops is still badfileops. E.g., * devfs_open() transiently create such situation to * facilitate csw d_fdopen(). * * Dupfdopen() handling in kern_openat() installs the * half-baked file into the process descriptor table, allowing * other thread to dereference it. Guard against the race by * checking f_ops. */ if (fp->f_vnode == NULL || fp->f_ops == &badfileops) { fdrop(fp, td); return (EINVAL); } *fpp = fp; return (0); } /* * Get an (NFS) file handle. */ #ifndef _SYS_SYSPROTO_H_ struct lgetfh_args { char *fname; fhandle_t *fhp; }; #endif int sys_lgetfh(struct thread *td, struct lgetfh_args *uap) { return (kern_getfhat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->fname, UIO_USERSPACE, uap->fhp)); } #ifndef _SYS_SYSPROTO_H_ struct getfh_args { char *fname; fhandle_t *fhp; }; #endif int sys_getfh(struct thread *td, struct getfh_args *uap) { return (kern_getfhat(td, 0, AT_FDCWD, uap->fname, UIO_USERSPACE, uap->fhp)); } /* * syscall for the rpc.lockd to use to translate an open descriptor into * a NFS file handle. * * warning: do not remove the priv_check() call or this becomes one giant * security hole. */ #ifndef _SYS_SYSPROTO_H_ struct getfhat_args { int fd; char *path; fhandle_t *fhp; int flags; }; #endif int sys_getfhat(struct thread *td, struct getfhat_args *uap) { if ((uap->flags & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); - return (kern_getfhat(td, uap->flags, uap->fd, uap->path ? uap->path : ".", - UIO_USERSPACE, uap->fhp)); + return (kern_getfhat(td, uap->flags, uap->fd, uap->path, UIO_USERSPACE, + uap->fhp)); } static int kern_getfhat(struct thread *td, int flags, int fd, const char *path, enum uio_seg pathseg, fhandle_t *fhp) { struct nameidata nd; fhandle_t fh; struct vnode *vp; int error; error = priv_check(td, PRIV_VFS_GETFH); if (error != 0) return (error); NDINIT_AT(&nd, LOOKUP, ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : FOLLOW) | ((flags & AT_BENEATH) != 0 ? BENEATH : 0) | LOCKLEAF | AUDITVNODE1, pathseg, path, fd, td); error = namei(&nd); if (error != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VOP_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error == 0) error = copyout(&fh, fhp, sizeof (fh)); return (error); } #ifndef _SYS_SYSPROTO_H_ struct fhlink_args { fhandle_t *fhp; const char *to; }; #endif int sys_fhlink(struct thread *td, struct fhlink_args *uap) { return (kern_fhlinkat(td, AT_FDCWD, uap->to, UIO_USERSPACE, uap->fhp)); } #ifndef _SYS_SYSPROTO_H_ struct fhlinkat_args { fhandle_t *fhp; int tofd; const char *to; }; #endif int sys_fhlinkat(struct thread *td, struct fhlinkat_args *uap) { return (kern_fhlinkat(td, uap->tofd, uap->to, UIO_USERSPACE, uap->fhp)); } static int kern_fhlinkat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, fhandle_t *fhp) { fhandle_t fh; struct mount *mp; struct vnode *vp; int error; error = priv_check(td, PRIV_VFS_GETFH); if (error != 0) return (error); error = copyin(fhp, &fh, sizeof(fh)); if (error != 0) return (error); do { bwillwrite(); if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) return (ESTALE); error = VFS_FHTOVP(mp, &fh.fh_fid, LK_SHARED, &vp); vfs_unbusy(mp); if (error != 0) return (error); VOP_UNLOCK(vp, 0); } while ((error = kern_linkat_vp(td, vp, fd, path, pathseg)) == EAGAIN); return (error); } #ifndef _SYS_SYSPROTO_H_ struct fhreadlink_args { fhandle_t *fhp; char *buf; size_t bufsize; }; #endif int sys_fhreadlink(struct thread *td, struct fhreadlink_args *uap) { fhandle_t fh; struct mount *mp; struct vnode *vp; int error; error = priv_check(td, PRIV_VFS_GETFH); if (error != 0) return (error); if (uap->bufsize > IOSIZE_MAX) return (EINVAL); error = copyin(uap->fhp, &fh, sizeof(fh)); if (error != 0) return (error); if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) return (ESTALE); error = VFS_FHTOVP(mp, &fh.fh_fid, LK_SHARED, &vp); vfs_unbusy(mp); if (error != 0) return (error); error = kern_readlink_vp(vp, uap->buf, UIO_USERSPACE, uap->bufsize, td); vput(vp); return (error); } /* * syscall for the rpc.lockd to use to translate a NFS file handle into an * open descriptor. * * warning: do not remove the priv_check() call or this becomes one giant * security hole. */ #ifndef _SYS_SYSPROTO_H_ struct fhopen_args { const struct fhandle *u_fhp; int flags; }; #endif int sys_fhopen(struct thread *td, struct fhopen_args *uap) { struct mount *mp; struct vnode *vp; struct fhandle fhp; struct file *fp; int fmode, error; int indx; error = priv_check(td, PRIV_VFS_FHOPEN); if (error != 0) return (error); indx = -1; fmode = FFLAGS(uap->flags); /* why not allow a non-read/write open for our lockd? */ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); error = copyin(uap->u_fhp, &fhp, sizeof(fhp)); if (error != 0) return(error); /* find the mount point */ mp = vfs_busyfs(&fhp.fh_fsid); if (mp == NULL) return (ESTALE); /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, LK_EXCLUSIVE, &vp); vfs_unbusy(mp); if (error != 0) return (error); error = falloc_noinstall(td, &fp); if (error != 0) { vput(vp); return (error); } /* * An extra reference on `fp' has been held for us by * falloc_noinstall(). */ #ifdef INVARIANTS td->td_dupfd = -1; #endif error = vn_open_vnode(vp, fmode, td->td_ucred, td, fp); if (error != 0) { KASSERT(fp->f_ops == &badfileops, ("VOP_OPEN in fhopen() set f_ops")); KASSERT(td->td_dupfd < 0, ("fhopen() encountered fdopen()")); vput(vp); goto bad; } #ifdef INVARIANTS td->td_dupfd = 0; #endif fp->f_vnode = vp; fp->f_seqcount = 1; finit(fp, (fmode & FMASK) | (fp->f_flag & FHASLOCK), DTYPE_VNODE, vp, &vnops); VOP_UNLOCK(vp, 0); if ((fmode & O_TRUNC) != 0) { error = fo_truncate(fp, 0, td->td_ucred, td); if (error != 0) goto bad; } error = finstall(td, fp, &indx, fmode, NULL); bad: fdrop(fp, td); td->td_retval[0] = indx; return (error); } /* * Stat an (NFS) file handle. */ #ifndef _SYS_SYSPROTO_H_ struct fhstat_args { struct fhandle *u_fhp; struct stat *sb; }; #endif int sys_fhstat(struct thread *td, struct fhstat_args *uap) { struct stat sb; struct fhandle fh; int error; error = copyin(uap->u_fhp, &fh, sizeof(fh)); if (error != 0) return (error); error = kern_fhstat(td, fh, &sb); if (error == 0) error = copyout(&sb, uap->sb, sizeof(sb)); return (error); } int kern_fhstat(struct thread *td, struct fhandle fh, struct stat *sb) { struct mount *mp; struct vnode *vp; int error; error = priv_check(td, PRIV_VFS_FHSTAT); if (error != 0) return (error); if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) return (ESTALE); error = VFS_FHTOVP(mp, &fh.fh_fid, LK_EXCLUSIVE, &vp); vfs_unbusy(mp); if (error != 0) return (error); error = vn_stat(vp, sb, td->td_ucred, NOCRED, td); vput(vp); return (error); } /* * Implement fstatfs() for (NFS) file handles. */ #ifndef _SYS_SYSPROTO_H_ struct fhstatfs_args { struct fhandle *u_fhp; struct statfs *buf; }; #endif int sys_fhstatfs(struct thread *td, struct fhstatfs_args *uap) { struct statfs *sfp; fhandle_t fh; int error; error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); if (error != 0) return (error); sfp = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); error = kern_fhstatfs(td, fh, sfp); if (error == 0) error = copyout(sfp, uap->buf, sizeof(*sfp)); free(sfp, M_STATFS); return (error); } int kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf) { struct statfs *sp; struct mount *mp; struct vnode *vp; int error; error = priv_check(td, PRIV_VFS_FHSTATFS); if (error != 0) return (error); if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) return (ESTALE); error = VFS_FHTOVP(mp, &fh.fh_fid, LK_EXCLUSIVE, &vp); if (error != 0) { vfs_unbusy(mp); return (error); } vput(vp); error = prison_canseemount(td->td_ucred, mp); if (error != 0) goto out; #ifdef MAC error = mac_mount_check_stat(td->td_ucred, mp); if (error != 0) goto out; #endif /* * Set these in case the underlying filesystem fails to do so. */ sp = &mp->mnt_stat; sp->f_version = STATFS_VERSION; sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp); if (error == 0) *buf = *sp; out: vfs_unbusy(mp); return (error); } int kern_posix_fallocate(struct thread *td, int fd, off_t offset, off_t len) { struct file *fp; struct mount *mp; struct vnode *vp; off_t olen, ooffset; int error; #ifdef AUDIT int audited_vnode1 = 0; #endif AUDIT_ARG_FD(fd); if (offset < 0 || len <= 0) return (EINVAL); /* Check for wrap. */ if (offset > OFF_MAX - len) return (EFBIG); AUDIT_ARG_FD(fd); error = fget(td, fd, &cap_pwrite_rights, &fp); if (error != 0) return (error); AUDIT_ARG_FILE(td->td_proc, fp); if ((fp->f_ops->fo_flags & DFLAG_SEEKABLE) == 0) { error = ESPIPE; goto out; } if ((fp->f_flag & FWRITE) == 0) { error = EBADF; goto out; } if (fp->f_type != DTYPE_VNODE) { error = ENODEV; goto out; } vp = fp->f_vnode; if (vp->v_type != VREG) { error = ENODEV; goto out; } /* Allocating blocks may take a long time, so iterate. */ for (;;) { olen = len; ooffset = offset; bwillwrite(); mp = NULL; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) break; error = vn_lock(vp, LK_EXCLUSIVE); if (error != 0) { vn_finished_write(mp); break; } #ifdef AUDIT if (!audited_vnode1) { AUDIT_ARG_VNODE1(vp); audited_vnode1 = 1; } #endif #ifdef MAC error = mac_vnode_check_write(td->td_ucred, fp->f_cred, vp); if (error == 0) #endif error = VOP_ALLOCATE(vp, &offset, &len); VOP_UNLOCK(vp, 0); vn_finished_write(mp); if (olen + ooffset != offset + len) { panic("offset + len changed from %jx/%jx to %jx/%jx", ooffset, olen, offset, len); } if (error != 0 || len == 0) break; KASSERT(olen > len, ("Iteration did not make progress?")); maybe_yield(); } out: fdrop(fp, td); return (error); } int sys_posix_fallocate(struct thread *td, struct posix_fallocate_args *uap) { int error; error = kern_posix_fallocate(td, uap->fd, uap->offset, uap->len); return (kern_posix_error(td, error)); } /* * Unlike madvise(2), we do not make a best effort to remember every * possible caching hint. Instead, we remember the last setting with * the exception that we will allow POSIX_FADV_NORMAL to adjust the * region of any current setting. */ int kern_posix_fadvise(struct thread *td, int fd, off_t offset, off_t len, int advice) { struct fadvise_info *fa, *new; struct file *fp; struct vnode *vp; off_t end; int error; if (offset < 0 || len < 0 || offset > OFF_MAX - len) return (EINVAL); AUDIT_ARG_VALUE(advice); switch (advice) { case POSIX_FADV_SEQUENTIAL: case POSIX_FADV_RANDOM: case POSIX_FADV_NOREUSE: new = malloc(sizeof(*fa), M_FADVISE, M_WAITOK); break; case POSIX_FADV_NORMAL: case POSIX_FADV_WILLNEED: case POSIX_FADV_DONTNEED: new = NULL; break; default: return (EINVAL); } /* XXX: CAP_POSIX_FADVISE? */ AUDIT_ARG_FD(fd); error = fget(td, fd, &cap_no_rights, &fp); if (error != 0) goto out; AUDIT_ARG_FILE(td->td_proc, fp); if ((fp->f_ops->fo_flags & DFLAG_SEEKABLE) == 0) { error = ESPIPE; goto out; } if (fp->f_type != DTYPE_VNODE) { error = ENODEV; goto out; } vp = fp->f_vnode; if (vp->v_type != VREG) { error = ENODEV; goto out; } if (len == 0) end = OFF_MAX; else end = offset + len - 1; switch (advice) { case POSIX_FADV_SEQUENTIAL: case POSIX_FADV_RANDOM: case POSIX_FADV_NOREUSE: /* * Try to merge any existing non-standard region with * this new region if possible, otherwise create a new * non-standard region for this request. */ mtx_pool_lock(mtxpool_sleep, fp); fa = fp->f_advice; if (fa != NULL && fa->fa_advice == advice && ((fa->fa_start <= end && fa->fa_end >= offset) || (end != OFF_MAX && fa->fa_start == end + 1) || (fa->fa_end != OFF_MAX && fa->fa_end + 1 == offset))) { if (offset < fa->fa_start) fa->fa_start = offset; if (end > fa->fa_end) fa->fa_end = end; } else { new->fa_advice = advice; new->fa_start = offset; new->fa_end = end; fp->f_advice = new; new = fa; } mtx_pool_unlock(mtxpool_sleep, fp); break; case POSIX_FADV_NORMAL: /* * If a the "normal" region overlaps with an existing * non-standard region, trim or remove the * non-standard region. */ mtx_pool_lock(mtxpool_sleep, fp); fa = fp->f_advice; if (fa != NULL) { if (offset <= fa->fa_start && end >= fa->fa_end) { new = fa; fp->f_advice = NULL; } else if (offset <= fa->fa_start && end >= fa->fa_start) fa->fa_start = end + 1; else if (offset <= fa->fa_end && end >= fa->fa_end) fa->fa_end = offset - 1; else if (offset >= fa->fa_start && end <= fa->fa_end) { /* * If the "normal" region is a middle * portion of the existing * non-standard region, just remove * the whole thing rather than picking * one side or the other to * preserve. */ new = fa; fp->f_advice = NULL; } } mtx_pool_unlock(mtxpool_sleep, fp); break; case POSIX_FADV_WILLNEED: case POSIX_FADV_DONTNEED: error = VOP_ADVISE(vp, offset, end, advice); break; } out: if (fp != NULL) fdrop(fp, td); free(new, M_FADVISE); return (error); } int sys_posix_fadvise(struct thread *td, struct posix_fadvise_args *uap) { int error; error = kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, uap->advice); return (kern_posix_error(td, error)); } Index: projects/clang700-import/sys/mips/conf/IDT.hints =================================================================== --- projects/clang700-import/sys/mips/conf/IDT.hints (revision 341812) +++ projects/clang700-import/sys/mips/conf/IDT.hints (nonexistent) @@ -1,22 +0,0 @@ -# $FreeBSD$ -# device.hints -hint.obio.0.at="nexus0" -hint.obio.0.maddr=0x0 -hint.obio.0.msize=0x1fffffff - -# host-to-pci bridge -hint.pcib.0.at="obio0" -# hint.pcib.0.maddr=0x11400000 -# hint.pcib.0.msize=0x100000 -# hint.pcib.0.io=0x11500000 -# hint.pcib.0.iosize=0x100000 - -# uart0 -hint.uart.0.at="obio0" -hint.uart.0.maddr=0x18058000 -hint.uart.0.msize=0x1C -hint.uart.0.irq=104 - -hint.kr.0.at="obio0" -hint.kr.0.maddr=0x18060000 -hint.kr.0.msize=0x10000 Property changes on: projects/clang700-import/sys/mips/conf/IDT.hints ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/clang700-import/sys/mips/conf/ADM5120.hints =================================================================== --- projects/clang700-import/sys/mips/conf/ADM5120.hints (revision 341812) +++ projects/clang700-import/sys/mips/conf/ADM5120.hints (nonexistent) @@ -1,24 +0,0 @@ -# $FreeBSD$ -# device.hints -hint.obio.0.at="nexus0" -hint.obio.0.maddr=0x0 -hint.obio.0.msize=0x1fffffff - -# host-to-pci bridge -hint.pcib.0.at="obio0" -hint.pcib.0.maddr=0x11400000 -hint.pcib.0.msize=0x100000 -hint.pcib.0.io=0x11500000 -hint.pcib.0.iosize=0x100000 - -# on-board switch engine -hint.admsw.0.at="obio0" -hint.admsw.0.maddr=0x12000000 -hint.admsw.0.msize=0x200000 -hint.admsw.0.irq=9 - -# uart0 -hint.uart.0.at="obio0" -hint.uart.0.maddr=0x12600000 -hint.uart.0.msize=0x200000 -hint.uart.0.irq=1 Property changes on: projects/clang700-import/sys/mips/conf/ADM5120.hints ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/clang700-import/sys/mips/include/atomic.h =================================================================== --- projects/clang700-import/sys/mips/include/atomic.h (revision 341812) +++ projects/clang700-import/sys/mips/include/atomic.h (revision 341813) @@ -1,758 +1,822 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: src/sys/alpha/include/atomic.h,v 1.21.2.3 2005/10/06 18:12:05 jhb * $FreeBSD$ */ #ifndef _MACHINE_ATOMIC_H_ #define _MACHINE_ATOMIC_H_ #ifndef _SYS_CDEFS_H_ #error this file needs sys/cdefs.h as a prerequisite #endif #include /* * Note: All the 64-bit atomic operations are only atomic when running * in 64-bit mode. It is assumed that code compiled for n32 and n64 * fits into this definition and no further safeties are needed. * * It is also assumed that the add, subtract and other arithmetic is * done on numbers not pointers. The special rules for n32 pointers * do not have atomic operations defined for them, but generally shouldn't * need atomic operations. */ #ifndef __MIPS_PLATFORM_SYNC_NOPS #define __MIPS_PLATFORM_SYNC_NOPS "" #endif static __inline void mips_sync(void) { __asm __volatile (".set noreorder\n" "\tsync\n" __MIPS_PLATFORM_SYNC_NOPS ".set reorder\n" : : : "memory"); } #define mb() mips_sync() #define wmb() mips_sync() #define rmb() mips_sync() /* * Various simple arithmetic on memory which is atomic in the presence * of interrupts and SMP safe. */ void atomic_set_8(__volatile uint8_t *, uint8_t); void atomic_clear_8(__volatile uint8_t *, uint8_t); void atomic_add_8(__volatile uint8_t *, uint8_t); void atomic_subtract_8(__volatile uint8_t *, uint8_t); void atomic_set_16(__volatile uint16_t *, uint16_t); void atomic_clear_16(__volatile uint16_t *, uint16_t); void atomic_add_16(__volatile uint16_t *, uint16_t); void atomic_subtract_16(__volatile uint16_t *, uint16_t); static __inline void atomic_set_32(__volatile uint32_t *p, uint32_t v) { uint32_t temp; __asm __volatile ( "1:\tll %0, %3\n\t" /* load old value */ "or %0, %2, %0\n\t" /* calculate new value */ "sc %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* spin if failed */ : "=&r" (temp), "=m" (*p) : "r" (v), "m" (*p) : "memory"); } static __inline void atomic_clear_32(__volatile uint32_t *p, uint32_t v) { uint32_t temp; v = ~v; __asm __volatile ( "1:\tll %0, %3\n\t" /* load old value */ "and %0, %2, %0\n\t" /* calculate new value */ "sc %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* spin if failed */ : "=&r" (temp), "=m" (*p) : "r" (v), "m" (*p) : "memory"); } static __inline void atomic_add_32(__volatile uint32_t *p, uint32_t v) { uint32_t temp; __asm __volatile ( "1:\tll %0, %3\n\t" /* load old value */ "addu %0, %2, %0\n\t" /* calculate new value */ "sc %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* spin if failed */ : "=&r" (temp), "=m" (*p) : "r" (v), "m" (*p) : "memory"); } static __inline void atomic_subtract_32(__volatile uint32_t *p, uint32_t v) { uint32_t temp; __asm __volatile ( "1:\tll %0, %3\n\t" /* load old value */ "subu %0, %2\n\t" /* calculate new value */ "sc %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* spin if failed */ : "=&r" (temp), "=m" (*p) : "r" (v), "m" (*p) : "memory"); } static __inline uint32_t atomic_readandclear_32(__volatile uint32_t *addr) { uint32_t result,temp; __asm __volatile ( "1:\tll %0,%3\n\t" /* load current value, asserting lock */ "li %1,0\n\t" /* value to store */ "sc %1,%2\n\t" /* attempt to store */ "beqz %1, 1b\n\t" /* if the store failed, spin */ : "=&r"(result), "=&r"(temp), "=m" (*addr) : "m" (*addr) : "memory"); return result; } static __inline uint32_t atomic_readandset_32(__volatile uint32_t *addr, uint32_t value) { uint32_t result,temp; __asm __volatile ( "1:\tll %0,%3\n\t" /* load current value, asserting lock */ "or %1,$0,%4\n\t" "sc %1,%2\n\t" /* attempt to store */ "beqz %1, 1b\n\t" /* if the store failed, spin */ : "=&r"(result), "=&r"(temp), "=m" (*addr) : "m" (*addr), "r" (value) : "memory"); return result; } #if defined(__mips_n64) || defined(__mips_n32) static __inline void atomic_set_64(__volatile uint64_t *p, uint64_t v) { uint64_t temp; __asm __volatile ( "1:\n\t" "lld %0, %3\n\t" /* load old value */ "or %0, %2, %0\n\t" /* calculate new value */ "scd %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* spin if failed */ : "=&r" (temp), "=m" (*p) : "r" (v), "m" (*p) : "memory"); } static __inline void atomic_clear_64(__volatile uint64_t *p, uint64_t v) { uint64_t temp; v = ~v; __asm __volatile ( "1:\n\t" "lld %0, %3\n\t" /* load old value */ "and %0, %2, %0\n\t" /* calculate new value */ "scd %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* spin if failed */ : "=&r" (temp), "=m" (*p) : "r" (v), "m" (*p) : "memory"); } static __inline void atomic_add_64(__volatile uint64_t *p, uint64_t v) { uint64_t temp; __asm __volatile ( "1:\n\t" "lld %0, %3\n\t" /* load old value */ "daddu %0, %2, %0\n\t" /* calculate new value */ "scd %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* spin if failed */ : "=&r" (temp), "=m" (*p) : "r" (v), "m" (*p) : "memory"); } static __inline void atomic_subtract_64(__volatile uint64_t *p, uint64_t v) { uint64_t temp; __asm __volatile ( "1:\n\t" "lld %0, %3\n\t" /* load old value */ "dsubu %0, %2\n\t" /* calculate new value */ "scd %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* spin if failed */ : "=&r" (temp), "=m" (*p) : "r" (v), "m" (*p) : "memory"); } static __inline uint64_t atomic_readandclear_64(__volatile uint64_t *addr) { uint64_t result,temp; __asm __volatile ( "1:\n\t" "lld %0, %3\n\t" /* load old value */ "li %1, 0\n\t" /* value to store */ "scd %1, %2\n\t" /* attempt to store */ "beqz %1, 1b\n\t" /* if the store failed, spin */ : "=&r"(result), "=&r"(temp), "=m" (*addr) : "m" (*addr) : "memory"); return result; } static __inline uint64_t atomic_readandset_64(__volatile uint64_t *addr, uint64_t value) { uint64_t result,temp; __asm __volatile ( "1:\n\t" "lld %0,%3\n\t" /* Load old value*/ "or %1,$0,%4\n\t" "scd %1,%2\n\t" /* attempt to store */ "beqz %1, 1b\n\t" /* if the store failed, spin */ : "=&r"(result), "=&r"(temp), "=m" (*addr) : "m" (*addr), "r" (value) : "memory"); return result; } #endif #define ATOMIC_ACQ_REL(NAME, WIDTH) \ static __inline void \ atomic_##NAME##_acq_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\ { \ atomic_##NAME##_##WIDTH(p, v); \ mips_sync(); \ } \ \ static __inline void \ atomic_##NAME##_rel_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\ { \ mips_sync(); \ atomic_##NAME##_##WIDTH(p, v); \ } /* Variants of simple arithmetic with memory barriers. */ ATOMIC_ACQ_REL(set, 8) ATOMIC_ACQ_REL(clear, 8) ATOMIC_ACQ_REL(add, 8) ATOMIC_ACQ_REL(subtract, 8) ATOMIC_ACQ_REL(set, 16) ATOMIC_ACQ_REL(clear, 16) ATOMIC_ACQ_REL(add, 16) ATOMIC_ACQ_REL(subtract, 16) ATOMIC_ACQ_REL(set, 32) ATOMIC_ACQ_REL(clear, 32) ATOMIC_ACQ_REL(add, 32) ATOMIC_ACQ_REL(subtract, 32) #if defined(__mips_n64) || defined(__mips_n32) ATOMIC_ACQ_REL(set, 64) ATOMIC_ACQ_REL(clear, 64) ATOMIC_ACQ_REL(add, 64) ATOMIC_ACQ_REL(subtract, 64) #endif #undef ATOMIC_ACQ_REL /* * We assume that a = b will do atomic loads and stores. */ #define ATOMIC_STORE_LOAD(WIDTH) \ static __inline uint##WIDTH##_t \ atomic_load_acq_##WIDTH(__volatile uint##WIDTH##_t *p) \ { \ uint##WIDTH##_t v; \ \ v = *p; \ mips_sync(); \ return (v); \ } \ \ static __inline void \ atomic_store_rel_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\ { \ mips_sync(); \ *p = v; \ } ATOMIC_STORE_LOAD(32) ATOMIC_STORE_LOAD(64) #undef ATOMIC_STORE_LOAD /* * Atomically compare the value stored at *p with cmpval and if the * two values are equal, update the value of *p with newval. Returns * zero if the compare failed, nonzero otherwise. */ static __inline uint32_t atomic_cmpset_32(__volatile uint32_t *p, uint32_t cmpval, uint32_t newval) { uint32_t ret; __asm __volatile ( "1:\tll %0, %4\n\t" /* load old value */ "bne %0, %2, 2f\n\t" /* compare */ "move %0, %3\n\t" /* value to store */ "sc %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* if it failed, spin */ "j 3f\n\t" "2:\n\t" "li %0, 0\n\t" "3:\n" : "=&r" (ret), "=m" (*p) : "r" (cmpval), "r" (newval), "m" (*p) : "memory"); return ret; } /* * Atomically compare the value stored at *p with cmpval and if the * two values are equal, update the value of *p with newval. Returns * zero if the compare failed, nonzero otherwise. */ static __inline uint32_t atomic_cmpset_acq_32(__volatile uint32_t *p, uint32_t cmpval, uint32_t newval) { int retval; retval = atomic_cmpset_32(p, cmpval, newval); mips_sync(); return (retval); } static __inline uint32_t atomic_cmpset_rel_32(__volatile uint32_t *p, uint32_t cmpval, uint32_t newval) { mips_sync(); return (atomic_cmpset_32(p, cmpval, newval)); } static __inline uint32_t atomic_fcmpset_32(__volatile uint32_t *p, uint32_t *cmpval, uint32_t newval) { uint32_t ret; __asm __volatile ( "1:\n\t" "ll %0, %1\n\t" /* load old value */ "bne %0, %4, 2f\n\t" /* compare */ "move %0, %3\n\t" /* value to store */ "sc %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* if it failed, spin */ "j 3f\n\t" "2:\n\t" "sw %0, %2\n\t" /* save old value */ "li %0, 0\n\t" "3:\n" : "=&r" (ret), "+m" (*p), "=m" (*cmpval) : "r" (newval), "r" (*cmpval) : "memory"); return ret; } static __inline uint32_t atomic_fcmpset_acq_32(__volatile uint32_t *p, uint32_t *cmpval, uint32_t newval) { int retval; retval = atomic_fcmpset_32(p, cmpval, newval); mips_sync(); return (retval); } static __inline uint32_t atomic_fcmpset_rel_32(__volatile uint32_t *p, uint32_t *cmpval, uint32_t newval) { mips_sync(); return (atomic_fcmpset_32(p, cmpval, newval)); } /* * Atomically add the value of v to the integer pointed to by p and return * the previous value of *p. */ static __inline uint32_t atomic_fetchadd_32(__volatile uint32_t *p, uint32_t v) { uint32_t value, temp; __asm __volatile ( "1:\tll %0, %1\n\t" /* load old value */ "addu %2, %3, %0\n\t" /* calculate new value */ "sc %2, %1\n\t" /* attempt to store */ "beqz %2, 1b\n\t" /* spin if failed */ : "=&r" (value), "=m" (*p), "=&r" (temp) : "r" (v), "m" (*p)); return (value); } #if defined(__mips_n64) || defined(__mips_n32) /* * Atomically compare the value stored at *p with cmpval and if the * two values are equal, update the value of *p with newval. Returns * zero if the compare failed, nonzero otherwise. */ static __inline uint64_t atomic_cmpset_64(__volatile uint64_t *p, uint64_t cmpval, uint64_t newval) { uint64_t ret; __asm __volatile ( "1:\n\t" "lld %0, %4\n\t" /* load old value */ "bne %0, %2, 2f\n\t" /* compare */ "move %0, %3\n\t" /* value to store */ "scd %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* if it failed, spin */ "j 3f\n\t" "2:\n\t" "li %0, 0\n\t" "3:\n" : "=&r" (ret), "=m" (*p) : "r" (cmpval), "r" (newval), "m" (*p) : "memory"); return ret; } /* * Atomically compare the value stored at *p with cmpval and if the * two values are equal, update the value of *p with newval. Returns * zero if the compare failed, nonzero otherwise. */ static __inline uint64_t atomic_cmpset_acq_64(__volatile uint64_t *p, uint64_t cmpval, uint64_t newval) { int retval; retval = atomic_cmpset_64(p, cmpval, newval); mips_sync(); return (retval); } static __inline uint64_t atomic_cmpset_rel_64(__volatile uint64_t *p, uint64_t cmpval, uint64_t newval) { mips_sync(); return (atomic_cmpset_64(p, cmpval, newval)); } static __inline uint32_t atomic_fcmpset_64(__volatile uint64_t *p, uint64_t *cmpval, uint64_t newval) { uint32_t ret; __asm __volatile ( "1:\n\t" "lld %0, %1\n\t" /* load old value */ "bne %0, %4, 2f\n\t" /* compare */ "move %0, %3\n\t" /* value to store */ "scd %0, %1\n\t" /* attempt to store */ "beqz %0, 1b\n\t" /* if it failed, spin */ "j 3f\n\t" "2:\n\t" "sd %0, %2\n\t" /* save old value */ "li %0, 0\n\t" "3:\n" : "=&r" (ret), "+m" (*p), "=m" (*cmpval) : "r" (newval), "r" (*cmpval) : "memory"); return ret; } static __inline uint64_t atomic_fcmpset_acq_64(__volatile uint64_t *p, uint64_t *cmpval, uint64_t newval) { int retval; retval = atomic_fcmpset_64(p, cmpval, newval); mips_sync(); return (retval); } static __inline uint64_t atomic_fcmpset_rel_64(__volatile uint64_t *p, uint64_t *cmpval, uint64_t newval) { mips_sync(); return (atomic_fcmpset_64(p, cmpval, newval)); } /* * Atomically add the value of v to the integer pointed to by p and return * the previous value of *p. */ static __inline uint64_t atomic_fetchadd_64(__volatile uint64_t *p, uint64_t v) { uint64_t value, temp; __asm __volatile ( "1:\n\t" "lld %0, %1\n\t" /* load old value */ "daddu %2, %3, %0\n\t" /* calculate new value */ "scd %2, %1\n\t" /* attempt to store */ "beqz %2, 1b\n\t" /* spin if failed */ : "=&r" (value), "=m" (*p), "=&r" (temp) : "r" (v), "m" (*p)); return (value); } #endif static __inline void atomic_thread_fence_acq(void) { mips_sync(); } static __inline void atomic_thread_fence_rel(void) { mips_sync(); } static __inline void atomic_thread_fence_acq_rel(void) { mips_sync(); } static __inline void atomic_thread_fence_seq_cst(void) { mips_sync(); } /* Operations on chars. */ #define atomic_set_char atomic_set_8 #define atomic_set_acq_char atomic_set_acq_8 #define atomic_set_rel_char atomic_set_rel_8 #define atomic_clear_char atomic_clear_8 #define atomic_clear_acq_char atomic_clear_acq_8 #define atomic_clear_rel_char atomic_clear_rel_8 #define atomic_add_char atomic_add_8 #define atomic_add_acq_char atomic_add_acq_8 #define atomic_add_rel_char atomic_add_rel_8 #define atomic_subtract_char atomic_subtract_8 #define atomic_subtract_acq_char atomic_subtract_acq_8 #define atomic_subtract_rel_char atomic_subtract_rel_8 /* Operations on shorts. */ #define atomic_set_short atomic_set_16 #define atomic_set_acq_short atomic_set_acq_16 #define atomic_set_rel_short atomic_set_rel_16 #define atomic_clear_short atomic_clear_16 #define atomic_clear_acq_short atomic_clear_acq_16 #define atomic_clear_rel_short atomic_clear_rel_16 #define atomic_add_short atomic_add_16 #define atomic_add_acq_short atomic_add_acq_16 #define atomic_add_rel_short atomic_add_rel_16 #define atomic_subtract_short atomic_subtract_16 #define atomic_subtract_acq_short atomic_subtract_acq_16 #define atomic_subtract_rel_short atomic_subtract_rel_16 /* Operations on ints. */ #define atomic_set_int atomic_set_32 #define atomic_set_acq_int atomic_set_acq_32 #define atomic_set_rel_int atomic_set_rel_32 #define atomic_clear_int atomic_clear_32 #define atomic_clear_acq_int atomic_clear_acq_32 #define atomic_clear_rel_int atomic_clear_rel_32 #define atomic_add_int atomic_add_32 #define atomic_add_acq_int atomic_add_acq_32 #define atomic_add_rel_int atomic_add_rel_32 #define atomic_subtract_int atomic_subtract_32 #define atomic_subtract_acq_int atomic_subtract_acq_32 #define atomic_subtract_rel_int atomic_subtract_rel_32 #define atomic_cmpset_int atomic_cmpset_32 #define atomic_cmpset_acq_int atomic_cmpset_acq_32 #define atomic_cmpset_rel_int atomic_cmpset_rel_32 #define atomic_fcmpset_int atomic_fcmpset_32 #define atomic_fcmpset_acq_int atomic_fcmpset_acq_32 #define atomic_fcmpset_rel_int atomic_fcmpset_rel_32 #define atomic_load_acq_int atomic_load_acq_32 #define atomic_store_rel_int atomic_store_rel_32 #define atomic_readandclear_int atomic_readandclear_32 #define atomic_readandset_int atomic_readandset_32 #define atomic_fetchadd_int atomic_fetchadd_32 /* * I think the following is right, even for n32. For n32 the pointers * are still 32-bits, so we need to operate on them as 32-bit quantities, * even though they are sign extended in operation. For longs, there's * no question because they are always 32-bits. */ #ifdef __mips_n64 /* Operations on longs. */ #define atomic_set_long atomic_set_64 #define atomic_set_acq_long atomic_set_acq_64 #define atomic_set_rel_long atomic_set_rel_64 #define atomic_clear_long atomic_clear_64 #define atomic_clear_acq_long atomic_clear_acq_64 #define atomic_clear_rel_long atomic_clear_rel_64 #define atomic_add_long atomic_add_64 #define atomic_add_acq_long atomic_add_acq_64 #define atomic_add_rel_long atomic_add_rel_64 #define atomic_subtract_long atomic_subtract_64 #define atomic_subtract_acq_long atomic_subtract_acq_64 #define atomic_subtract_rel_long atomic_subtract_rel_64 #define atomic_cmpset_long atomic_cmpset_64 #define atomic_cmpset_acq_long atomic_cmpset_acq_64 #define atomic_cmpset_rel_long atomic_cmpset_rel_64 #define atomic_fcmpset_long atomic_fcmpset_64 #define atomic_fcmpset_acq_long atomic_fcmpset_acq_64 #define atomic_fcmpset_rel_long atomic_fcmpset_rel_64 #define atomic_load_acq_long atomic_load_acq_64 #define atomic_store_rel_long atomic_store_rel_64 #define atomic_fetchadd_long atomic_fetchadd_64 #define atomic_readandclear_long atomic_readandclear_64 #else /* !__mips_n64 */ /* Operations on longs. */ #define atomic_set_long(p, v) \ atomic_set_32((volatile u_int *)(p), (u_int)(v)) #define atomic_set_acq_long(p, v) \ atomic_set_acq_32((volatile u_int *)(p), (u_int)(v)) #define atomic_set_rel_long(p, v) \ atomic_set_rel_32((volatile u_int *)(p), (u_int)(v)) #define atomic_clear_long(p, v) \ atomic_clear_32((volatile u_int *)(p), (u_int)(v)) #define atomic_clear_acq_long(p, v) \ atomic_clear_acq_32((volatile u_int *)(p), (u_int)(v)) #define atomic_clear_rel_long(p, v) \ atomic_clear_rel_32((volatile u_int *)(p), (u_int)(v)) #define atomic_add_long(p, v) \ atomic_add_32((volatile u_int *)(p), (u_int)(v)) #define atomic_add_acq_long(p, v) \ atomic_add_32((volatile u_int *)(p), (u_int)(v)) #define atomic_add_rel_long(p, v) \ atomic_add_32((volatile u_int *)(p), (u_int)(v)) #define atomic_subtract_long(p, v) \ atomic_subtract_32((volatile u_int *)(p), (u_int)(v)) #define atomic_subtract_acq_long(p, v) \ atomic_subtract_acq_32((volatile u_int *)(p), (u_int)(v)) #define atomic_subtract_rel_long(p, v) \ atomic_subtract_rel_32((volatile u_int *)(p), (u_int)(v)) #define atomic_cmpset_long(p, cmpval, newval) \ atomic_cmpset_32((volatile u_int *)(p), (u_int)(cmpval), \ (u_int)(newval)) #define atomic_cmpset_acq_long(p, cmpval, newval) \ atomic_cmpset_acq_32((volatile u_int *)(p), (u_int)(cmpval), \ (u_int)(newval)) #define atomic_cmpset_rel_long(p, cmpval, newval) \ atomic_cmpset_rel_32((volatile u_int *)(p), (u_int)(cmpval), \ (u_int)(newval)) #define atomic_fcmpset_long(p, cmpval, newval) \ atomic_fcmpset_32((volatile u_int *)(p), (u_int *)(cmpval), \ (u_int)(newval)) #define atomic_fcmpset_acq_long(p, cmpval, newval) \ atomic_fcmpset_acq_32((volatile u_int *)(p), (u_int *)(cmpval), \ (u_int)(newval)) #define atomic_fcmpset_rel_long(p, cmpval, newval) \ atomic_fcmpset_rel_32((volatile u_int *)(p), (u_int *)(cmpval), \ (u_int)(newval)) #define atomic_load_acq_long(p) \ (u_long)atomic_load_acq_32((volatile u_int *)(p)) #define atomic_store_rel_long(p, v) \ atomic_store_rel_32((volatile u_int *)(p), (u_int)(v)) #define atomic_fetchadd_long(p, v) \ atomic_fetchadd_32((volatile u_int *)(p), (u_int)(v)) #define atomic_readandclear_long(p) \ atomic_readandclear_32((volatile u_int *)(p)) #endif /* __mips_n64 */ /* Operations on pointers. */ #define atomic_set_ptr atomic_set_long #define atomic_set_acq_ptr atomic_set_acq_long #define atomic_set_rel_ptr atomic_set_rel_long #define atomic_clear_ptr atomic_clear_long #define atomic_clear_acq_ptr atomic_clear_acq_long #define atomic_clear_rel_ptr atomic_clear_rel_long #define atomic_add_ptr atomic_add_long #define atomic_add_acq_ptr atomic_add_acq_long #define atomic_add_rel_ptr atomic_add_rel_long #define atomic_subtract_ptr atomic_subtract_long #define atomic_subtract_acq_ptr atomic_subtract_acq_long #define atomic_subtract_rel_ptr atomic_subtract_rel_long #define atomic_cmpset_ptr atomic_cmpset_long #define atomic_cmpset_acq_ptr atomic_cmpset_acq_long #define atomic_cmpset_rel_ptr atomic_cmpset_rel_long #define atomic_fcmpset_ptr atomic_fcmpset_long #define atomic_fcmpset_acq_ptr atomic_fcmpset_acq_long #define atomic_fcmpset_rel_ptr atomic_fcmpset_rel_long #define atomic_load_acq_ptr atomic_load_acq_long #define atomic_store_rel_ptr atomic_store_rel_long #define atomic_readandclear_ptr atomic_readandclear_long +static __inline unsigned int +atomic_swap_int(volatile unsigned int *ptr, const unsigned int value) +{ + unsigned int retval; + + retval = *ptr; + + while (!atomic_fcmpset_int(ptr, &retval, value)) + ; + return (retval); +} + +static __inline uint32_t +atomic_swap_32(volatile uint32_t *ptr, const uint32_t value) +{ + uint32_t retval; + + retval = *ptr; + + while (!atomic_fcmpset_32(ptr, &retval, value)) + ; + return (retval); +} + +#if defined(__mips_n64) || defined(__mips_n32) +static __inline uint64_t +atomic_swap_64(volatile uint64_t *ptr, const uint64_t value) +{ + uint64_t retval; + + retval = *ptr; + + while (!atomic_fcmpset_64(ptr, &retval, value)) + ; + return (retval); +} +#endif + +static __inline unsigned long +atomic_swap_long(volatile unsigned long *ptr, const unsigned long value) +{ + unsigned long retval; + + retval = *ptr; + + while (!atomic_fcmpset_32((volatile uint32_t *)ptr, + (uint32_t *)&retval, value)) + ; + return (retval); +} + +static __inline uintptr_t +atomic_swap_ptr(volatile uintptr_t *ptr, const uintptr_t value) +{ + uintptr_t retval; + + retval = *ptr; + + while (!atomic_fcmpset_32((volatile uint32_t *)ptr, + (uint32_t *)&retval, value)) + ; + return (retval); +} + #endif /* ! _MACHINE_ATOMIC_H_ */ Index: projects/clang700-import/sys/modules/Makefile =================================================================== --- projects/clang700-import/sys/modules/Makefile (revision 341812) +++ projects/clang700-import/sys/modules/Makefile (revision 341813) @@ -1,845 +1,845 @@ # $FreeBSD$ SYSDIR?=${SRCTOP}/sys .include "${SYSDIR}/conf/kern.opts.mk" SUBDIR_PARALLEL= # Modules that include binary-only blobs of microcode should be selectable by # MK_SOURCELESS_UCODE option (see below). .if defined(MODULES_OVERRIDE) && !defined(ALL_MODULES) SUBDIR=${MODULES_OVERRIDE} .else SUBDIR= \ ${_3dfx} \ ${_3dfx_linux} \ ${_aac} \ ${_aacraid} \ accf_data \ accf_dns \ accf_http \ acl_nfs4 \ acl_posix1e \ ${_acpi} \ ae \ ${_aesni} \ age \ ${_agp} \ ahci \ aic7xxx \ alc \ ale \ alq \ ${_amd_ecc_inject} \ ${_amdgpio} \ ${_amdsbwd} \ ${_amdsmn} \ ${_amdtemp} \ amr \ ${_an} \ ${_aout} \ ${_apm} \ ${_arcmsr} \ ${_allwinner} \ ${_armv8crypto} \ ${_asmc} \ ata \ ath \ ath_dfs \ ath_hal \ ath_hal_ar5210 \ ath_hal_ar5211 \ ath_hal_ar5212 \ ath_hal_ar5416 \ ath_hal_ar9300 \ ath_main \ ath_rate \ ath_pci \ ${_autofs} \ ${_auxio} \ ${_bce} \ ${_bcm283x_clkman} \ ${_bcm283x_pwm} \ bfe \ bge \ bhnd \ ${_bxe} \ ${_bios} \ ${_bktr} \ ${_blake2} \ ${_bm} \ bnxt \ bridgestp \ bwi \ bwn \ ${_bytgpio} \ ${_chvgpio} \ cam \ ${_cardbus} \ ${_carp} \ cas \ ${_cbb} \ cc \ ${_ccp} \ cd9660 \ cd9660_iconv \ ${_ce} \ ${_cfi} \ ${_chromebook_platform} \ ${_ciss} \ cloudabi \ ${_cloudabi32} \ ${_cloudabi64} \ ${_cmx} \ ${_coff} \ ${_coretemp} \ ${_cp} \ ${_cpsw} \ ${_cpuctl} \ ${_cpufreq} \ ${_crypto} \ ${_cryptodev} \ ${_cs} \ ${_ctau} \ ctl \ ${_cxgb} \ ${_cxgbe} \ dc \ dcons \ dcons_crom \ de \ ${_dpms} \ ${_drm} \ ${_drm2} \ dummynet \ ${_ed} \ ${_efirt} \ ${_em} \ ${_ena} \ ${_ep} \ ${_epic} \ esp \ ${_et} \ evdev \ ${_ex} \ ${_exca} \ ext2fs \ fdc \ fdescfs \ ${_fe} \ ${_ffec} \ filemon \ firewire \ firmware \ fuse \ ${_fxp} \ gem \ geom \ ${_glxiic} \ ${_glxsb} \ gpio \ hifn \ hme \ ${_hpt27xx} \ ${_hptiop} \ ${_hptmv} \ ${_hptnr} \ ${_hptrr} \ hwpmc \ ${_hwpmc_mips24k} \ ${_hwpmc_mips74k} \ ${_hyperv} \ i2c \ ${_iavf} \ ${_ibcore} \ ${_ibcs2} \ ${_ichwd} \ ${_ida} \ if_bridge \ if_disc \ if_edsc \ ${_if_enc} \ if_epair \ ${_if_gif} \ ${_if_gre} \ ${_if_me} \ if_lagg \ ${_if_ndis} \ ${_if_stf} \ if_tap \ if_tun \ if_vlan \ if_vxlan \ ${_iir} \ imgact_binmisc \ ${_intelspi} \ ${_io} \ ${_ioat} \ ${_ipoib} \ ${_ipdivert} \ ${_ipfilter} \ ${_ipfw} \ ipfw_nat \ ${_ipfw_nat64} \ ${_ipfw_nptv6} \ ${_ipfw_pmod} \ ${_ipmi} \ ip6_mroute_mod \ ip_mroute_mod \ ${_ips} \ ${_ipsec} \ ${_ipw} \ ${_ipwfw} \ ${_isci} \ ${_iser} \ isp \ ${_ispfw} \ ${_iwi} \ ${_iwifw} \ ${_iwm} \ ${_iwmfw} \ ${_iwn} \ ${_iwnfw} \ ${_ix} \ ${_ixv} \ ${_ixl} \ jme \ kbdmux \ kgssapi \ kgssapi_krb5 \ khelp \ krpc \ ksyms \ le \ lge \ libalias \ libiconv \ libmchain \ ${_linux} \ ${_linux_common} \ ${_linux64} \ linuxkpi \ ${_lio} \ lpt \ mac_biba \ mac_bsdextended \ mac_ifoff \ mac_lomac \ mac_mls \ mac_none \ mac_ntpd \ mac_partition \ mac_portacl \ mac_seeotheruids \ mac_stub \ mac_test \ malo \ md \ mdio \ mem \ mfi \ mii \ mlx \ ${_mlx4} \ ${_mlx4ib} \ ${_mlx4en} \ ${_mlx5} \ ${_mlx5en} \ ${_mlx5ib} \ ${_mly} \ mmc \ mmcsd \ ${_mpr} \ ${_mps} \ mpt \ mqueue \ mrsas \ msdosfs \ msdosfs_iconv \ msk \ ${_mthca} \ mvs \ mwl \ ${_mwlfw} \ mxge \ my \ ${_nandfs} \ ${_nandsim} \ ${_nctgpio} \ ${_ndis} \ ${_netgraph} \ ${_nfe} \ nfscl \ nfscommon \ nfsd \ nfslock \ nfslockd \ nfssvc \ nge \ nmdm \ nullfs \ ${_ntb} \ ${_nvd} \ ${_nvdimm} \ ${_nvme} \ ${_nvram} \ oce \ ${_ocs_fc} \ otus \ ${_otusfw} \ ow \ ${_padlock} \ ${_padlock_rng} \ ${_pccard} \ ${_pcfclock} \ pcn \ ${_pf} \ ${_pflog} \ ${_pfsync} \ plip \ ${_pms} \ ppbus \ ppc \ ppi \ pps \ procfs \ proto \ pseudofs \ ${_pst} \ pty \ puc \ ${_qlxge} \ ${_qlxgb} \ ${_qlxgbe} \ ${_qlnx} \ ral \ ${_ralfw} \ ${_random_fortuna} \ ${_random_other} \ rc4 \ ${_rdma} \ ${_rdrand_rng} \ re \ rl \ ${_rockchip} \ rtwn \ rtwn_pci \ rtwn_usb \ ${_rtwnfw} \ ${_s3} \ ${_safe} \ ${_sbni} \ scc \ sdhci \ ${_sdhci_acpi} \ sdhci_pci \ sem \ send \ ${_sf} \ ${_sfxge} \ sge \ ${_sgx} \ ${_sgx_linux} \ siftr \ siis \ sis \ sk \ ${_smartpqi} \ smbfs \ sn \ snp \ sound \ ${_speaker} \ spi \ ${_splash} \ ${_sppp} \ ste \ stge \ ${_sym} \ ${_syscons} \ sysvipc \ tcp \ ${_ti} \ tl \ tmpfs \ ${_toecore} \ ${_tpm} \ trm \ ${_twa} \ twe \ tws \ tx \ ${_txp} \ uart \ ubsec \ udf \ udf_iconv \ ufs \ uinput \ unionfs \ usb \ ${_vesa} \ ${_virtio} \ vge \ ${_viawd} \ videomode \ vkbd \ ${_vmm} \ ${_vmware} \ ${_vpo} \ vr \ vte \ vx \ wb \ ${_wbwd} \ ${_wi} \ wlan \ wlan_acl \ wlan_amrr \ wlan_ccmp \ wlan_rssadapt \ wlan_tkip \ wlan_wep \ wlan_xauth \ ${_wpi} \ ${_wpifw} \ ${_x86bios} \ ${_xe} \ xl \ zlib .if ${MK_AUTOFS} != "no" || defined(ALL_MODULES) _autofs= autofs .endif .if ${MK_CDDL} != "no" || defined(ALL_MODULES) .if (${MACHINE_CPUARCH} != "arm" || ${MACHINE_ARCH:Marmv[67]*} != "") && \ ${MACHINE_CPUARCH} != "mips" && \ ${MACHINE_CPUARCH} != "sparc64" SUBDIR+= dtrace .endif SUBDIR+= opensolaris .endif .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) .if exists(${SRCTOP}/sys/opencrypto) _crypto= crypto _cryptodev= cryptodev _random_fortuna=random_fortuna _random_other= random_other .endif .endif .if ${MK_CUSE} != "no" || defined(ALL_MODULES) SUBDIR+= cuse .endif .if (${MK_INET_SUPPORT} != "no" || ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _carp= carp _toecore= toecore _if_enc= if_enc _if_gif= if_gif _if_gre= if_gre _ipfw_pmod= ipfw_pmod .if ${MK_IPSEC_SUPPORT} != "no" _ipsec= ipsec .endif .endif .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _if_stf= if_stf .endif .if ${MK_INET_SUPPORT} != "no" || defined(ALL_MODULES) _if_me= if_me _ipdivert= ipdivert _ipfw= ipfw .if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) _ipfw_nat64= ipfw_nat64 .endif .endif .if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) _ipfw_nptv6= ipfw_nptv6 .endif .if ${MK_IPFILTER} != "no" || defined(ALL_MODULES) _ipfilter= ipfilter .endif .if ${MK_ISCSI} != "no" || defined(ALL_MODULES) SUBDIR+= cfiscsi SUBDIR+= iscsi SUBDIR+= iscsi_initiator .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "i386" SUBDIR+= linprocfs SUBDIR+= linsysfs .endif .if ${MK_NAND} != "no" || defined(ALL_MODULES) _nandfs= nandfs _nandsim= nandsim .endif .if ${MK_NETGRAPH} != "no" || defined(ALL_MODULES) _netgraph= netgraph .endif .if (${MK_PF} != "no" && (${MK_INET_SUPPORT} != "no" || \ ${MK_INET6_SUPPORT} != "no")) || defined(ALL_MODULES) _pf= pf _pflog= pflog .if ${MK_INET_SUPPORT} != "no" _pfsync= pfsync .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" _bce= bce _fxp= fxp _ispfw= ispfw _sf= sf _ti= ti _txp= txp .if ${MACHINE_CPUARCH} != "mips" _mwlfw= mwlfw _otusfw= otusfw _ralfw= ralfw _rtwnfw= rtwnfw .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && \ ${MACHINE_ARCH} != "powerpc" && ${MACHINE_ARCH} != "powerpcspe" && \ ${MACHINE_CPUARCH} != "riscv" _cxgbe= cxgbe .endif # These rely on 64bit atomics -.if ${MACHINE_ARCH} != "powerpc" && ${MACHINE_ARCH} != "powerpcspc" && \ +.if ${MACHINE_ARCH} != "powerpc" && ${MACHINE_ARCH} != "powerpcspe" && \ ${MACHINE_CPUARCH} != "mips" _mps= mps _mpr= mpr .endif .if ${MK_TESTS} != "no" || defined(ALL_MODULES) SUBDIR+= tests .endif .if ${MK_ZFS} != "no" || defined(ALL_MODULES) SUBDIR+= zfs .endif .if (${MACHINE_CPUARCH} == "mips" && ${MACHINE_ARCH:Mmips64} == "") _hwpmc_mips24k= hwpmc_mips24k _hwpmc_mips74k= hwpmc_mips74k .endif .if ${MACHINE_CPUARCH} != "aarch64" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && ${MACHINE_CPUARCH} != "powerpc" && \ ${MACHINE_CPUARCH} != "riscv" _syscons= syscons _vpo= vpo .endif .if ${MACHINE_CPUARCH} != "mips" # no BUS_SPACE_UNSPECIFIED # No barrier instruction support (specific to this driver) _sym= sym # intr_disable() is a macro, causes problems .if ${MK_SOURCELESS_UCODE} != "no" _cxgb= cxgb .endif .endif .if ${MACHINE_CPUARCH} == "aarch64" _allwinner= allwinner _armv8crypto= armv8crypto _efirt= efirt _em= em _rockchip= rockchip .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _agp= agp _an= an _aout= aout _bios= bios _bktr= bktr .if ${MK_SOURCELESS_UCODE} != "no" _bxe= bxe .endif _cardbus= cardbus _cbb= cbb _cpuctl= cpuctl _cpufreq= cpufreq _cs= cs _dpms= dpms .if ${MK_MODULE_DRM} != "no" _drm= drm .endif .if ${MK_MODULE_DRM2} != "no" _drm2= drm2 .endif _ed= ed _em= em _ena= ena _ep= ep _et= et _exca= exca _fe= fe .if ${MK_OFED} != "no" || defined(ALL_MODULES) _ibcore= ibcore .endif _if_ndis= if_ndis _io= io .if ${MK_OFED} != "no" || defined(ALL_MODULES) _ipoib= ipoib _iser= iser .endif _ix= ix _ixv= ixv _linux= linux .if ${MK_SOURCELESS_UCODE} != "no" _lio= lio .endif _nctgpio= nctgpio _ndis= ndis _ocs_fc= ocs_fc _pccard= pccard .if ${MK_OFED} != "no" || defined(ALL_MODULES) _rdma= rdma .endif _safe= safe _speaker= speaker _splash= splash _sppp= sppp _vmware= vmware _wbwd= wbwd _wi= wi _xe= xe _aac= aac _aacraid= aacraid _acpi= acpi .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) .if ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} > 40201 _aesni= aesni .endif .endif _amd_ecc_inject=amd_ecc_inject _amdsbwd= amdsbwd _amdsmn= amdsmn _amdtemp= amdtemp _arcmsr= arcmsr _asmc= asmc .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _blake2= blake2 .endif _bytgpio= bytgpio _chvgpio= chvgpio _ciss= ciss _chromebook_platform= chromebook_platform _cmx= cmx _coretemp= coretemp .if ${MK_SOURCELESS_HOST} != "no" _hpt27xx= hpt27xx .endif _hptiop= hptiop .if ${MK_SOURCELESS_HOST} != "no" _hptmv= hptmv _hptnr= hptnr _hptrr= hptrr .endif _hyperv= hyperv _ichwd= ichwd _ida= ida _iir= iir _intelspi= intelspi _ipmi= ipmi _ips= ips _isci= isci _ipw= ipw _iwi= iwi _iwm= iwm _iwn= iwn .if ${MK_SOURCELESS_UCODE} != "no" _ipwfw= ipwfw _iwifw= iwifw _iwmfw= iwmfw _iwnfw= iwnfw .endif _mlx4= mlx4 _mlx5= mlx5 .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _mlx4en= mlx4en _mlx5en= mlx5en .endif .if ${MK_OFED} != "no" || defined(ALL_MODULES) _mthca= mthca _mlx4ib= mlx4ib _mlx5ib= mlx5ib .endif _mly= mly _nfe= nfe _nvd= nvd _nvme= nvme _nvram= nvram .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _padlock= padlock _padlock_rng= padlock_rng _rdrand_rng= rdrand_rng .endif _s3= s3 _sdhci_acpi= sdhci_acpi _tpm= tpm _twa= twa _vesa= vesa _viawd= viawd _virtio= virtio _wpi= wpi .if ${MK_SOURCELESS_UCODE} != "no" _wpifw= wpifw .endif _x86bios= x86bios .endif .if ${MACHINE_CPUARCH} == "amd64" _amdgpio= amdgpio _ccp= ccp _efirt= efirt _iavf= iavf _ioat= ioat _ixl= ixl _linux64= linux64 _linux_common= linux_common _ntb= ntb _nvdimm= nvdimm _pms= pms _qlxge= qlxge _qlxgb= qlxgb .if ${MK_SOURCELESS_UCODE} != "no" _qlxgbe= qlxgbe _qlnx= qlnx .endif _sfxge= sfxge _sgx= sgx _sgx_linux= sgx_linux _smartpqi= smartpqi .if ${MK_BHYVE} != "no" || defined(ALL_MODULES) _vmm= vmm .endif .endif .if ${MACHINE_CPUARCH} == "i386" # XXX some of these can move to the general case when de-i386'ed # XXX some of these can move now, but are untested on other architectures. _3dfx= 3dfx _3dfx_linux= 3dfx_linux _apm= apm .if ${MK_SOURCELESS_UCODE} != "no" _ce= ce .endif _coff= coff .if ${MK_SOURCELESS_UCODE} != "no" _cp= cp .endif _glxiic= glxiic _glxsb= glxsb #_ibcs2= ibcs2 _pcfclock= pcfclock _pst= pst _sbni= sbni .if ${MK_SOURCELESS_UCODE} != "no" _ctau= ctau .endif _ex= ex .endif .if ${MACHINE_CPUARCH} == "arm" _cfi= cfi _cpsw= cpsw .endif .if ${MACHINE_CPUARCH} == "powerpc" _agp= agp _an= an _bm= bm _cardbus= cardbus _cbb= cbb _cfi= cfi _cpufreq= cpufreq .if ${MK_MODULE_DRM} != "no" _drm= drm .endif _exca= exca _ffec= ffec _nvd= nvd _nvme= nvme _pccard= pccard _wi= wi .endif .if ${MACHINE_ARCH} == "powerpc64" .if ${MK_MODULE_DRM2} != "no" _drm2= drm2 .endif _ipmi= ipmi .endif .if ${MACHINE_ARCH} == "powerpc64" || ${MACHINE_ARCH} == "powerpc" # Don't build powermac_nvram for powerpcspe, it's never supported. _nvram= powermac_nvram .endif .if ${MACHINE_CPUARCH} == "sparc64" _auxio= auxio _em= em _epic= epic .endif .if (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_ARCH:Marmv[67]*} != "" || ${MACHINE_CPUARCH} == "i386") _cloudabi32= cloudabi32 .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" _cloudabi64= cloudabi64 .endif .endif .if ${MACHINE_ARCH:Marmv[67]*} != "" || ${MACHINE_CPUARCH} == "aarch64" _bcm283x_clkman= bcm283x_clkman _bcm283x_pwm= bcm283x_pwm .endif SUBDIR+=${MODULES_EXTRA} .for reject in ${WITHOUT_MODULES} SUBDIR:= ${SUBDIR:N${reject}} .endfor # Calling kldxref(8) for each module is expensive. .if !defined(NO_XREF) .MAKEFLAGS+= -DNO_XREF afterinstall: .PHONY @if type kldxref >/dev/null 2>&1; then \ ${ECHO} kldxref ${DESTDIR}${KMODDIR}; \ kldxref ${DESTDIR}${KMODDIR}; \ fi .endif .include "${SYSDIR}/conf/config.mk" SUBDIR:= ${SUBDIR:u:O} .include Index: projects/clang700-import/sys/powerpc/aim/aim_machdep.c =================================================================== --- projects/clang700-import/sys/powerpc/aim/aim_machdep.c (revision 341812) +++ projects/clang700-import/sys/powerpc/aim/aim_machdep.c (revision 341813) @@ -1,695 +1,696 @@ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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) 2001 Benno Rice * 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 Benno Rice ``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 TOOLS GMBH 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. * $NetBSD: machdep.c,v 1.74.2.1 2000/11/01 16:13:48 tv Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_kstack_pages.h" #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __powerpc64__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __powerpc64__ #include "mmu_oea64.h" #endif #ifndef __powerpc64__ struct bat battable[16]; #endif #ifndef __powerpc64__ /* Bits for running on 64-bit systems in 32-bit mode. */ extern void *testppc64, *testppc64size; extern void *restorebridge, *restorebridgesize; extern void *rfid_patch, *rfi_patch1, *rfi_patch2; extern void *trapcode64; extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; #endif extern void *rstcode, *rstcodeend; extern void *trapcode, *trapcodeend; extern void *hypertrapcode, *hypertrapcodeend; extern void *generictrap, *generictrap64; extern void *alitrap, *aliend; extern void *dsitrap, *dsiend; extern void *decrint, *decrsize; extern void *extint, *extsize; extern void *dblow, *dbend; extern void *imisstrap, *imisssize; extern void *dlmisstrap, *dlmisssize; extern void *dsmisstrap, *dsmisssize; extern void *ap_pcpu; extern void __restartkernel(vm_offset_t, vm_offset_t, vm_offset_t, void *, uint32_t, register_t offset, register_t msr); void aim_early_init(vm_offset_t fdt, vm_offset_t toc, vm_offset_t ofentry, void *mdp, uint32_t mdp_cookie); void aim_cpu_init(vm_offset_t toc); void aim_early_init(vm_offset_t fdt, vm_offset_t toc, vm_offset_t ofentry, void *mdp, uint32_t mdp_cookie) { register_t scratch; /* * If running from an FDT, make sure we are in real mode to avoid * tromping on firmware page tables. Everything in the kernel assumes * 1:1 mappings out of firmware, so this won't break anything not * already broken. This doesn't work if there is live OF, since OF * may internally use non-1:1 mappings. */ if (ofentry == 0) mtmsr(mfmsr() & ~(PSL_IR | PSL_DR)); #ifdef __powerpc64__ /* * If in real mode, relocate to high memory so that the kernel * can execute from the direct map. */ if (!(mfmsr() & PSL_DR) && (vm_offset_t)&aim_early_init < DMAP_BASE_ADDRESS) __restartkernel(fdt, 0, ofentry, mdp, mdp_cookie, DMAP_BASE_ADDRESS, mfmsr()); #endif /* Various very early CPU fix ups */ switch (mfpvr() >> 16) { /* * PowerPC 970 CPUs have a misfeature requested by Apple that * makes them pretend they have a 32-byte cacheline. Turn this * off before we measure the cacheline size. */ case IBM970: case IBM970FX: case IBM970MP: case IBM970GX: scratch = mfspr(SPR_HID5); scratch &= ~HID5_970_DCBZ_SIZE_HI; mtspr(SPR_HID5, scratch); break; #ifdef __powerpc64__ case IBMPOWER7: case IBMPOWER7PLUS: case IBMPOWER8: case IBMPOWER8E: case IBMPOWER9: /* XXX: get from ibm,slb-size in device tree */ n_slbs = 32; break; #endif } } void aim_cpu_init(vm_offset_t toc) { size_t trap_offset, trapsize; vm_offset_t trap; register_t msr; uint8_t *cache_check; int cacheline_warn; #ifndef __powerpc64__ register_t scratch; int ppc64; #endif trap_offset = 0; cacheline_warn = 0; /* General setup for AIM CPUs */ psl_kernset = PSL_EE | PSL_ME | PSL_IR | PSL_DR | PSL_RI; #ifdef __powerpc64__ psl_kernset |= PSL_SF; if (mfmsr() & PSL_HV) psl_kernset |= PSL_HV; #endif psl_userset = psl_kernset | PSL_PR; #ifdef __powerpc64__ psl_userset32 = psl_userset & ~PSL_SF; #endif /* Bits that users aren't allowed to change */ psl_userstatic = ~(PSL_VEC | PSL_FP | PSL_FE0 | PSL_FE1); /* * Mask bits from the SRR1 that aren't really the MSR: * Bits 1-4, 10-15 (ppc32), 33-36, 42-47 (ppc64) */ psl_userstatic &= ~0x783f0000UL; /* * Initialize the interrupt tables and figure out our cache line * size and whether or not we need the 64-bit bridge code. */ /* * Disable translation in case the vector area hasn't been * mapped (G5). Note that no OFW calls can be made until * translation is re-enabled. */ msr = mfmsr(); mtmsr((msr & ~(PSL_IR | PSL_DR)) | PSL_RI); /* * Measure the cacheline size using dcbz * * Use EXC_PGM as a playground. We are about to overwrite it * anyway, we know it exists, and we know it is cache-aligned. */ cache_check = (void *)EXC_PGM; for (cacheline_size = 0; cacheline_size < 0x100; cacheline_size++) cache_check[cacheline_size] = 0xff; __asm __volatile("dcbz 0,%0":: "r" (cache_check) : "memory"); /* Find the first byte dcbz did not zero to get the cache line size */ for (cacheline_size = 0; cacheline_size < 0x100 && cache_check[cacheline_size] == 0; cacheline_size++); /* Work around psim bug */ if (cacheline_size == 0) { cacheline_warn = 1; cacheline_size = 32; } #ifndef __powerpc64__ /* * Figure out whether we need to use the 64 bit PMAP. This works by * executing an instruction that is only legal on 64-bit PPC (mtmsrd), * and setting ppc64 = 0 if that causes a trap. */ ppc64 = 1; bcopy(&testppc64, (void *)EXC_PGM, (size_t)&testppc64size); __syncicache((void *)EXC_PGM, (size_t)&testppc64size); __asm __volatile("\ mfmsr %0; \ mtsprg2 %1; \ \ mtmsrd %0; \ mfsprg2 %1;" : "=r"(scratch), "=r"(ppc64)); if (ppc64) cpu_features |= PPC_FEATURE_64; /* * Now copy restorebridge into all the handlers, if necessary, * and set up the trap tables. */ if (cpu_features & PPC_FEATURE_64) { /* Patch the two instances of rfi -> rfid */ bcopy(&rfid_patch,&rfi_patch1,4); #ifdef KDB /* rfi_patch2 is at the end of dbleave */ bcopy(&rfid_patch,&rfi_patch2,4); #endif } #else /* powerpc64 */ cpu_features |= PPC_FEATURE_64; #endif trapsize = (size_t)&trapcodeend - (size_t)&trapcode; /* * Copy generic handler into every possible trap. Special cases will get * different ones in a minute. */ for (trap = EXC_RST; trap < EXC_LAST; trap += 0x20) bcopy(&trapcode, (void *)trap, trapsize); #ifndef __powerpc64__ if (cpu_features & PPC_FEATURE_64) { /* * Copy a code snippet to restore 32-bit bridge mode * to the top of every non-generic trap handler */ trap_offset += (size_t)&restorebridgesize; bcopy(&restorebridge, (void *)EXC_RST, trap_offset); bcopy(&restorebridge, (void *)EXC_DSI, trap_offset); bcopy(&restorebridge, (void *)EXC_ALI, trap_offset); bcopy(&restorebridge, (void *)EXC_PGM, trap_offset); bcopy(&restorebridge, (void *)EXC_MCHK, trap_offset); bcopy(&restorebridge, (void *)EXC_TRC, trap_offset); bcopy(&restorebridge, (void *)EXC_BPT, trap_offset); } #else trapsize = (size_t)&hypertrapcodeend - (size_t)&hypertrapcode; bcopy(&hypertrapcode, (void *)(EXC_HEA + trap_offset), trapsize); bcopy(&hypertrapcode, (void *)(EXC_HMI + trap_offset), trapsize); bcopy(&hypertrapcode, (void *)(EXC_HVI + trap_offset), trapsize); + bcopy(&hypertrapcode, (void *)(EXC_SOFT_PATCH + trap_offset), trapsize); #endif bcopy(&rstcode, (void *)(EXC_RST + trap_offset), (size_t)&rstcodeend - (size_t)&rstcode); #ifdef KDB bcopy(&dblow, (void *)(EXC_MCHK + trap_offset), (size_t)&dbend - (size_t)&dblow); bcopy(&dblow, (void *)(EXC_PGM + trap_offset), (size_t)&dbend - (size_t)&dblow); bcopy(&dblow, (void *)(EXC_TRC + trap_offset), (size_t)&dbend - (size_t)&dblow); bcopy(&dblow, (void *)(EXC_BPT + trap_offset), (size_t)&dbend - (size_t)&dblow); #endif bcopy(&alitrap, (void *)(EXC_ALI + trap_offset), (size_t)&aliend - (size_t)&alitrap); bcopy(&dsitrap, (void *)(EXC_DSI + trap_offset), (size_t)&dsiend - (size_t)&dsitrap); #ifdef __powerpc64__ /* Set TOC base so that the interrupt code can get at it */ *((void **)TRAP_GENTRAP) = &generictrap; *((register_t *)TRAP_TOCBASE) = toc; #else /* Set branch address for trap code */ if (cpu_features & PPC_FEATURE_64) *((void **)TRAP_GENTRAP) = &generictrap64; else *((void **)TRAP_GENTRAP) = &generictrap; *((void **)TRAP_TOCBASE) = _GLOBAL_OFFSET_TABLE_; /* G2-specific TLB miss helper handlers */ bcopy(&imisstrap, (void *)EXC_IMISS, (size_t)&imisssize); bcopy(&dlmisstrap, (void *)EXC_DLMISS, (size_t)&dlmisssize); bcopy(&dsmisstrap, (void *)EXC_DSMISS, (size_t)&dsmisssize); #endif __syncicache(EXC_RSVD, EXC_LAST - EXC_RSVD); /* * Restore MSR */ mtmsr(msr); /* Warn if cachline size was not determined */ if (cacheline_warn == 1) { printf("WARNING: cacheline size undetermined, setting to 32\n"); } /* * Initialise virtual memory. Use BUS_PROBE_GENERIC priority * in case the platform module had a better idea of what we * should do. */ if (cpu_features2 & PPC_FEATURE2_ARCH_3_00) pmap_mmu_install(MMU_TYPE_P9H, BUS_PROBE_GENERIC); else if (cpu_features & PPC_FEATURE_64) pmap_mmu_install(MMU_TYPE_G5, BUS_PROBE_GENERIC); else pmap_mmu_install(MMU_TYPE_OEA, BUS_PROBE_GENERIC); } /* * Shutdown the CPU as much as possible. */ void cpu_halt(void) { OF_exit(); } int ptrace_single_step(struct thread *td) { struct trapframe *tf; tf = td->td_frame; tf->srr1 |= PSL_SE; return (0); } int ptrace_clear_single_step(struct thread *td) { struct trapframe *tf; tf = td->td_frame; tf->srr1 &= ~PSL_SE; return (0); } void kdb_cpu_clear_singlestep(void) { kdb_frame->srr1 &= ~PSL_SE; } void kdb_cpu_set_singlestep(void) { kdb_frame->srr1 |= PSL_SE; } /* * Initialise a struct pcpu. */ void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t sz) { #ifdef __powerpc64__ /* Copy the SLB contents from the current CPU */ memcpy(pcpu->pc_aim.slb, PCPU_GET(aim.slb), sizeof(pcpu->pc_aim.slb)); #endif } #ifndef __powerpc64__ uint64_t va_to_vsid(pmap_t pm, vm_offset_t va) { return ((pm->pm_sr[(uintptr_t)va >> ADDR_SR_SHFT]) & SR_VSID_MASK); } #endif /* * These functions need to provide addresses that both (a) work in real mode * (or whatever mode/circumstances the kernel is in in early boot (now)) and * (b) can still, in principle, work once the kernel is going. Because these * rely on existing mappings/real mode, unmap is a no-op. */ vm_offset_t pmap_early_io_map(vm_paddr_t pa, vm_size_t size) { KASSERT(!pmap_bootstrapped, ("Not available after PMAP started!")); /* * If we have the MMU up in early boot, assume it is 1:1. Otherwise, * try to get the address in a memory region compatible with the * direct map for efficiency later. */ if (mfmsr() & PSL_DR) return (pa); else return (DMAP_BASE_ADDRESS + pa); } void pmap_early_io_unmap(vm_offset_t va, vm_size_t size) { KASSERT(!pmap_bootstrapped, ("Not available after PMAP started!")); } /* From p3-53 of the MPC7450 RISC Microprocessor Family Reference Manual */ void flush_disable_caches(void) { register_t msr; register_t msscr0; register_t cache_reg; volatile uint32_t *memp; uint32_t temp; int i; int x; msr = mfmsr(); powerpc_sync(); mtmsr(msr & ~(PSL_EE | PSL_DR)); msscr0 = mfspr(SPR_MSSCR0); msscr0 &= ~MSSCR0_L2PFE; mtspr(SPR_MSSCR0, msscr0); powerpc_sync(); isync(); __asm__ __volatile__("dssall; sync"); powerpc_sync(); isync(); __asm__ __volatile__("dcbf 0,%0" :: "r"(0)); __asm__ __volatile__("dcbf 0,%0" :: "r"(0)); __asm__ __volatile__("dcbf 0,%0" :: "r"(0)); /* Lock the L1 Data cache. */ mtspr(SPR_LDSTCR, mfspr(SPR_LDSTCR) | 0xFF); powerpc_sync(); isync(); mtspr(SPR_LDSTCR, 0); /* * Perform this in two stages: Flush the cache starting in RAM, then do it * from ROM. */ memp = (volatile uint32_t *)0x00000000; for (i = 0; i < 128 * 1024; i++) { temp = *memp; __asm__ __volatile__("dcbf 0,%0" :: "r"(memp)); memp += 32/sizeof(*memp); } memp = (volatile uint32_t *)0xfff00000; x = 0xfe; for (; x != 0xff;) { mtspr(SPR_LDSTCR, x); for (i = 0; i < 128; i++) { temp = *memp; __asm__ __volatile__("dcbf 0,%0" :: "r"(memp)); memp += 32/sizeof(*memp); } x = ((x << 1) | 1) & 0xff; } mtspr(SPR_LDSTCR, 0); cache_reg = mfspr(SPR_L2CR); if (cache_reg & L2CR_L2E) { cache_reg &= ~(L2CR_L2IO_7450 | L2CR_L2DO_7450); mtspr(SPR_L2CR, cache_reg); powerpc_sync(); mtspr(SPR_L2CR, cache_reg | L2CR_L2HWF); while (mfspr(SPR_L2CR) & L2CR_L2HWF) ; /* Busy wait for cache to flush */ powerpc_sync(); cache_reg &= ~L2CR_L2E; mtspr(SPR_L2CR, cache_reg); powerpc_sync(); mtspr(SPR_L2CR, cache_reg | L2CR_L2I); powerpc_sync(); while (mfspr(SPR_L2CR) & L2CR_L2I) ; /* Busy wait for L2 cache invalidate */ powerpc_sync(); } cache_reg = mfspr(SPR_L3CR); if (cache_reg & L3CR_L3E) { cache_reg &= ~(L3CR_L3IO | L3CR_L3DO); mtspr(SPR_L3CR, cache_reg); powerpc_sync(); mtspr(SPR_L3CR, cache_reg | L3CR_L3HWF); while (mfspr(SPR_L3CR) & L3CR_L3HWF) ; /* Busy wait for cache to flush */ powerpc_sync(); cache_reg &= ~L3CR_L3E; mtspr(SPR_L3CR, cache_reg); powerpc_sync(); mtspr(SPR_L3CR, cache_reg | L3CR_L3I); powerpc_sync(); while (mfspr(SPR_L3CR) & L3CR_L3I) ; /* Busy wait for L3 cache invalidate */ powerpc_sync(); } mtspr(SPR_HID0, mfspr(SPR_HID0) & ~HID0_DCE); powerpc_sync(); isync(); mtmsr(msr); } void cpu_sleep() { static u_quad_t timebase = 0; static register_t sprgs[4]; static register_t srrs[2]; jmp_buf resetjb; struct thread *fputd; struct thread *vectd; register_t hid0; register_t msr; register_t saved_msr; ap_pcpu = pcpup; PCPU_SET(restore, &resetjb); saved_msr = mfmsr(); fputd = PCPU_GET(fputhread); vectd = PCPU_GET(vecthread); if (fputd != NULL) save_fpu(fputd); if (vectd != NULL) save_vec(vectd); if (setjmp(resetjb) == 0) { sprgs[0] = mfspr(SPR_SPRG0); sprgs[1] = mfspr(SPR_SPRG1); sprgs[2] = mfspr(SPR_SPRG2); sprgs[3] = mfspr(SPR_SPRG3); srrs[0] = mfspr(SPR_SRR0); srrs[1] = mfspr(SPR_SRR1); timebase = mftb(); powerpc_sync(); flush_disable_caches(); hid0 = mfspr(SPR_HID0); hid0 = (hid0 & ~(HID0_DOZE | HID0_NAP)) | HID0_SLEEP; powerpc_sync(); isync(); msr = mfmsr() | PSL_POW; mtspr(SPR_HID0, hid0); powerpc_sync(); while (1) mtmsr(msr); } platform_smp_timebase_sync(timebase, 0); PCPU_SET(curthread, curthread); PCPU_SET(curpcb, curthread->td_pcb); pmap_activate(curthread); powerpc_sync(); mtspr(SPR_SPRG0, sprgs[0]); mtspr(SPR_SPRG1, sprgs[1]); mtspr(SPR_SPRG2, sprgs[2]); mtspr(SPR_SPRG3, sprgs[3]); mtspr(SPR_SRR0, srrs[0]); mtspr(SPR_SRR1, srrs[1]); mtmsr(saved_msr); if (fputd == curthread) enable_fpu(curthread); if (vectd == curthread) enable_vec(curthread); powerpc_sync(); } Index: projects/clang700-import/sys/powerpc/booke/locore.S =================================================================== --- projects/clang700-import/sys/powerpc/booke/locore.S (revision 341812) +++ projects/clang700-import/sys/powerpc/booke/locore.S (revision 341813) @@ -1,942 +1,937 @@ /*- * Copyright (C) 2007-2009 Semihalf, Rafal Jaworowski * Copyright (C) 2006 Semihalf, Marian Balakowicz * 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. * * $FreeBSD$ */ #include "assym.inc" #include "opt_hwpmc_hooks.h" #include #include #include #include #include #include #include #include #define TMPSTACKSZ 16384 #ifdef __powerpc64__ #define GET_TOCBASE(r) \ mfspr r, SPR_SPRG8 #define TOC_RESTORE nop #define CMPI cmpdi #define CMPL cmpld #define LOAD ld #define LOADX ldarx #define STORE std #define STOREX stdcx. #define STU stdu #define CALLSIZE 48 #define REDZONE 288 #define THREAD_REG %r13 #define ADDR(x) \ .llong x #define WORD_SIZE 8 #else #define GET_TOCBASE(r) #define TOC_RESTORE #define CMPI cmpwi #define CMPL cmplw #define LOAD lwz #define LOADX lwarx #define STOREX stwcx. #define STORE stw #define STU stwu #define CALLSIZE 8 #define REDZONE 0 #define THREAD_REG %r2 #define ADDR(x) \ .long x #define WORD_SIZE 4 #endif .text .globl btext btext: /* * This symbol is here for the benefit of kvm_mkdb, and is supposed to * mark the start of kernel text. */ .globl kernel_text kernel_text: /* * Startup entry. Note, this must be the first thing in the text segment! */ .text .globl __start __start: /* * Assumptions on the boot loader: * - System memory starts from physical address 0 * - It's mapped by a single TLB1 entry * - TLB1 mapping is 1:1 pa to va * - Kernel is loaded at 64MB boundary * - All PID registers are set to the same value * - CPU is running in AS=0 * * Registers contents provided by the loader(8): * r1 : stack pointer * r3 : metadata pointer * * We rearrange the TLB1 layout as follows: * - Find TLB1 entry we started in * - Make sure it's protected, invalidate other entries * - Create temp entry in the second AS (make sure it's not TLB[1]) * - Switch to temp mapping * - Map 64MB of RAM in TLB1[1] * - Use AS=0, set EPN to VM_MIN_KERNEL_ADDRESS and RPN to kernel load address * - Switch to TLB1[1] mapping * - Invalidate temp mapping * * locore registers use: * r1 : stack pointer * r2 : trace pointer (AP only, for early diagnostics) * r3-r27 : scratch registers * r28 : temp TLB1 entry * r29 : initial TLB1 entry we started in * r30-r31 : arguments (metadata pointer) */ /* * Keep arguments in r30 & r31 for later use. */ mr %r30, %r3 mr %r31, %r4 /* * Initial cleanup */ li %r3, PSL_DE /* Keep debug exceptions for CodeWarrior. */ #ifdef __powerpc64__ oris %r3, %r3, PSL_CM@h #endif mtmsr %r3 isync /* * Initial HIDs configuration */ 1: mfpvr %r3 rlwinm %r3, %r3, 16, 16, 31 lis %r4, HID0_E500_DEFAULT_SET@h ori %r4, %r4, HID0_E500_DEFAULT_SET@l /* Check for e500mc and e5500 */ cmpli 0, 0, %r3, FSL_E500mc bne 2f lis %r4, HID0_E500MC_DEFAULT_SET@h ori %r4, %r4, HID0_E500MC_DEFAULT_SET@l b 3f 2: cmpli 0, 0, %r3, FSL_E5500 bne 3f lis %r4, HID0_E5500_DEFAULT_SET@h ori %r4, %r4, HID0_E5500_DEFAULT_SET@l 3: mtspr SPR_HID0, %r4 isync /* * E500mc and E5500 do not have HID1 register, so skip HID1 setup on * this core. */ cmpli 0, 0, %r3, FSL_E500mc beq 1f cmpli 0, 0, %r3, FSL_E5500 beq 1f cmpli 0, 0, %r3, FSL_E6500 beq 1f lis %r3, HID1_E500_DEFAULT_SET@h ori %r3, %r3, HID1_E500_DEFAULT_SET@l mtspr SPR_HID1, %r3 isync 1: /* Invalidate all entries in TLB0 */ li %r3, 0 bl tlb_inval_all cmpwi %r30, 0 beq done_mapping /* * Locate the TLB1 entry that maps this code */ bl 1f 1: mflr %r3 bl tlb1_find_current /* the entry found is returned in r29 */ bl tlb1_inval_all_but_current /* * Create temporary mapping in AS=1 and switch to it */ bl tlb1_temp_mapping_as1 mfmsr %r3 ori %r3, %r3, (PSL_IS | PSL_DS) bl 2f 2: mflr %r4 addi %r4, %r4, (3f - 2b) mtspr SPR_SRR0, %r4 mtspr SPR_SRR1, %r3 rfi /* Switch context */ /* * Invalidate initial entry */ 3: mr %r3, %r29 bl tlb1_inval_entry /* * Setup final mapping in TLB1[1] and switch to it */ /* Final kernel mapping, map in 64 MB of RAM */ lis %r3, MAS0_TLBSEL1@h /* Select TLB1 */ li %r4, 0 /* Entry 0 */ rlwimi %r3, %r4, 16, 10, 15 mtspr SPR_MAS0, %r3 isync li %r3, (TLB_SIZE_64M << MAS1_TSIZE_SHIFT)@l oris %r3, %r3, (MAS1_VALID | MAS1_IPROT)@h mtspr SPR_MAS1, %r3 /* note TS was not filled, so it's TS=0 */ isync LOAD_ADDR(%r3, VM_MIN_KERNEL_ADDRESS) ori %r3, %r3, (_TLB_ENTRY_SHARED | MAS2_M)@l /* WIMGE = 0b00100 */ mtspr SPR_MAS2, %r3 isync /* Discover phys load address */ bl 3f 3: mflr %r4 /* Use current address */ rlwinm %r4, %r4, 0, 0, 5 /* 64MB alignment mask */ ori %r4, %r4, (MAS3_SX | MAS3_SW | MAS3_SR)@l mtspr SPR_MAS3, %r4 /* Set RPN and protection */ isync bl zero_mas7 bl zero_mas8 tlbwe isync msync /* Switch to the above TLB1[1] mapping */ bl 4f 4: mflr %r4 #ifdef __powerpc64__ clrldi %r4, %r4, 38 clrrdi %r3, %r3, 12 #else rlwinm %r4, %r4, 0, 6, 31 /* Current offset from kernel load address */ rlwinm %r3, %r3, 0, 0, 19 #endif add %r4, %r4, %r3 /* Convert to kernel virtual address */ addi %r4, %r4, (5f - 4b) li %r3, PSL_DE /* Note AS=0 */ #ifdef __powerpc64__ oris %r3, %r3, PSL_CM@h #endif mtspr SPR_SRR0, %r4 mtspr SPR_SRR1, %r3 rfi /* * Invalidate temp mapping */ 5: mr %r3, %r28 bl tlb1_inval_entry done_mapping: #ifdef __powerpc64__ /* Set up the TOC pointer */ b 0f .align 3 0: nop bl 1f .llong __tocbase + 0x8000 - . 1: mflr %r2 ld %r1,0(%r2) add %r2,%r1,%r2 mtspr SPR_SPRG8, %r2 /* Get load offset */ ld %r31,-0x8000(%r2) /* First TOC entry is TOC base */ subf %r31,%r31,%r2 /* Subtract from real TOC base to get base */ /* Set up the stack pointer */ ld %r1,TOC_REF(tmpstack)(%r2) addi %r1,%r1,TMPSTACKSZ-96 add %r1,%r1,%r31 bl 1f .llong _DYNAMIC-. 1: mflr %r3 ld %r4,0(%r3) add %r3,%r4,%r3 mr %r4,%r31 #else /* * Setup a temporary stack */ bl 1f .long tmpstack-. 1: mflr %r1 lwz %r2,0(%r1) add %r1,%r1,%r2 addi %r1, %r1, (TMPSTACKSZ - 16) /* * Relocate kernel */ bl 1f .long _DYNAMIC-. .long _GLOBAL_OFFSET_TABLE_-. 1: mflr %r5 lwz %r3,0(%r5) /* _DYNAMIC in %r3 */ add %r3,%r3,%r5 lwz %r4,4(%r5) /* GOT pointer */ add %r4,%r4,%r5 lwz %r4,4(%r4) /* got[0] is _DYNAMIC link addr */ subf %r4,%r4,%r3 /* subtract to calculate relocbase */ #endif bl CNAME(elf_reloc_self) TOC_RESTORE /* * Initialise exception vector offsets */ bl CNAME(ivor_setup) TOC_RESTORE /* * Set up arguments and jump to system initialization code */ mr %r3, %r30 mr %r4, %r31 /* Prepare core */ bl CNAME(booke_init) TOC_RESTORE /* Switch to thread0.td_kstack now */ mr %r1, %r3 li %r3, 0 STORE %r3, 0(%r1) /* Machine independet part, does not return */ bl CNAME(mi_startup) TOC_RESTORE /* NOT REACHED */ 5: b 5b #ifdef SMP /************************************************************************/ /* AP Boot page */ /************************************************************************/ .text .globl __boot_page .align 12 __boot_page: bl 1f .globl bp_trace bp_trace: .long 0 .globl bp_kernload bp_kernload: .long 0 /* * Initial configuration */ 1: mflr %r31 /* r31 hold the address of bp_trace */ /* Set HIDs */ mfpvr %r3 rlwinm %r3, %r3, 16, 16, 31 /* HID0 for E500 is default */ lis %r4, HID0_E500_DEFAULT_SET@h ori %r4, %r4, HID0_E500_DEFAULT_SET@l cmpli 0, 0, %r3, FSL_E500mc bne 2f lis %r4, HID0_E500MC_DEFAULT_SET@h ori %r4, %r4, HID0_E500MC_DEFAULT_SET@l b 3f 2: cmpli 0, 0, %r3, FSL_E5500 bne 3f lis %r4, HID0_E5500_DEFAULT_SET@h ori %r4, %r4, HID0_E5500_DEFAULT_SET@l 3: mtspr SPR_HID0, %r4 isync /* Enable branch prediction */ li %r3, BUCSR_BPEN mtspr SPR_BUCSR, %r3 isync /* Invalidate all entries in TLB0 */ li %r3, 0 bl tlb_inval_all /* * Find TLB1 entry which is translating us now */ bl 2f 2: mflr %r3 bl tlb1_find_current /* the entry number found is in r29 */ bl tlb1_inval_all_but_current /* * Create temporary translation in AS=1 and switch to it */ bl tlb1_temp_mapping_as1 mfmsr %r3 ori %r3, %r3, (PSL_IS | PSL_DS) #ifdef __powerpc64__ oris %r3, %r3, PSL_CM@h #endif bl 3f 3: mflr %r4 addi %r4, %r4, (4f - 3b) mtspr SPR_SRR0, %r4 mtspr SPR_SRR1, %r3 rfi /* Switch context */ /* * Invalidate initial entry */ 4: mr %r3, %r29 bl tlb1_inval_entry /* * Setup final mapping in TLB1[1] and switch to it */ /* Final kernel mapping, map in 64 MB of RAM */ lis %r3, MAS0_TLBSEL1@h /* Select TLB1 */ li %r4, 0 /* Entry 0 */ rlwimi %r3, %r4, 16, 4, 15 mtspr SPR_MAS0, %r3 isync li %r3, (TLB_SIZE_64M << MAS1_TSIZE_SHIFT)@l oris %r3, %r3, (MAS1_VALID | MAS1_IPROT)@h mtspr SPR_MAS1, %r3 /* note TS was not filled, so it's TS=0 */ isync LOAD_ADDR(%r3, VM_MIN_KERNEL_ADDRESS) ori %r3, %r3, (_TLB_ENTRY_SHARED | MAS2_M)@l /* WIMGE = 0b00100 */ mtspr SPR_MAS2, %r3 isync /* Retrieve kernel load [physical] address from bp_kernload */ #ifdef __powerpc64__ b 0f .align 3 0: nop #endif bl 5f ADDR(bp_kernload) ADDR(__boot_page) 5: mflr %r3 #ifdef __powerpc64__ ld %r4, 0(%r3) ld %r5, 8(%r3) clrrdi %r3, %r3, 12 #else lwz %r4, 0(%r3) lwz %r5, 4(%r3) rlwinm %r3, %r3, 0, 0, 19 #endif sub %r4, %r4, %r5 /* offset of bp_kernload within __boot_page */ lwzx %r3, %r4, %r3 /* Set RPN and protection */ ori %r3, %r3, (MAS3_SX | MAS3_SW | MAS3_SR)@l mtspr SPR_MAS3, %r3 isync bl zero_mas7 bl zero_mas8 tlbwe isync msync /* Switch to the final mapping */ bl 6f 6: mflr %r3 rlwinm %r3, %r3, 0, 0xfff /* Offset from boot page start */ add %r3, %r3, %r5 /* Make this virtual address */ addi %r3, %r3, (7f - 6b) #ifdef __powerpc64__ lis %r4, PSL_CM@h /* Note AS=0 */ #else li %r4, 0 /* Note AS=0 */ #endif mtspr SPR_SRR0, %r3 mtspr SPR_SRR1, %r4 rfi 7: /* * At this point we're running at virtual addresses VM_MIN_KERNEL_ADDRESS and * beyond so it's allowed to directly access all locations the kernel was linked * against. */ /* * Invalidate temp mapping */ mr %r3, %r28 bl tlb1_inval_entry #ifdef __powerpc64__ /* Set up the TOC pointer */ b 0f .align 3 0: nop bl 1f .llong __tocbase + 0x8000 - . 1: mflr %r2 ld %r1,0(%r2) add %r2,%r1,%r2 mtspr SPR_SPRG8, %r2 - /* Get load offset */ - ld %r31,-0x8000(%r2) /* First TOC entry is TOC base */ - subf %r31,%r31,%r2 /* Subtract from real TOC base to get base */ - /* Set up the stack pointer */ ld %r1,TOC_REF(tmpstack)(%r2) addi %r1,%r1,TMPSTACKSZ-96 - add %r1,%r1,%r31 #else /* * Setup a temporary stack */ bl 1f .long tmpstack-. 1: mflr %r1 lwz %r2,0(%r1) add %r1,%r1,%r2 stw %r1, 0(%r1) addi %r1, %r1, (TMPSTACKSZ - 16) #endif /* * Initialise exception vector offsets */ bl CNAME(ivor_setup) TOC_RESTORE /* * Assign our pcpu instance */ bl 1f .long ap_pcpu-. 1: mflr %r4 lwz %r3, 0(%r4) add %r3, %r3, %r4 LOAD %r3, 0(%r3) mtsprg0 %r3 bl CNAME(pmap_bootstrap_ap) TOC_RESTORE bl CNAME(cpudep_ap_bootstrap) TOC_RESTORE /* Switch to the idle thread's kstack */ mr %r1, %r3 bl CNAME(machdep_ap_bootstrap) TOC_RESTORE /* NOT REACHED */ 6: b 6b #endif /* SMP */ #if defined (BOOKE_E500) /* * Invalidate all entries in the given TLB. * * r3 TLBSEL */ tlb_inval_all: rlwinm %r3, %r3, 3, (1 << 3) /* TLBSEL */ ori %r3, %r3, (1 << 2) /* INVALL */ tlbivax 0, %r3 isync msync tlbsync msync blr /* * expects address to look up in r3, returns entry number in r29 * * FIXME: the hidden assumption is we are now running in AS=0, but we should * retrieve actual AS from MSR[IS|DS] and put it in MAS6[SAS] */ tlb1_find_current: mfspr %r17, SPR_PID0 slwi %r17, %r17, MAS6_SPID0_SHIFT mtspr SPR_MAS6, %r17 isync tlbsx 0, %r3 mfspr %r17, SPR_MAS0 rlwinm %r29, %r17, 16, 26, 31 /* MAS0[ESEL] -> r29 */ /* Make sure we have IPROT set on the entry */ mfspr %r17, SPR_MAS1 oris %r17, %r17, MAS1_IPROT@h mtspr SPR_MAS1, %r17 isync tlbwe isync msync blr /* * Invalidates a single entry in TLB1. * * r3 ESEL * r4-r5 scratched */ tlb1_inval_entry: lis %r4, MAS0_TLBSEL1@h /* Select TLB1 */ rlwimi %r4, %r3, 16, 10, 15 /* Select our entry */ mtspr SPR_MAS0, %r4 isync tlbre li %r5, 0 /* MAS1[V] = 0 */ mtspr SPR_MAS1, %r5 isync tlbwe isync msync blr /* * r29 current entry number * r28 returned temp entry * r3-r5 scratched */ tlb1_temp_mapping_as1: /* Read our current translation */ lis %r3, MAS0_TLBSEL1@h /* Select TLB1 */ rlwimi %r3, %r29, 16, 10, 15 /* Select our current entry */ mtspr SPR_MAS0, %r3 isync tlbre /* * Prepare and write temp entry * * FIXME this is not robust against overflow i.e. when the current * entry is the last in TLB1 */ lis %r3, MAS0_TLBSEL1@h /* Select TLB1 */ addi %r28, %r29, 1 /* Use next entry. */ rlwimi %r3, %r28, 16, 10, 15 /* Select temp entry */ mtspr SPR_MAS0, %r3 isync mfspr %r5, SPR_MAS1 li %r4, 1 /* AS=1 */ rlwimi %r5, %r4, 12, 19, 19 li %r4, 0 /* Global mapping, TID=0 */ rlwimi %r5, %r4, 16, 8, 15 oris %r5, %r5, (MAS1_VALID | MAS1_IPROT)@h mtspr SPR_MAS1, %r5 isync mflr %r3 bl zero_mas7 bl zero_mas8 mtlr %r3 tlbwe isync msync blr /* * Loops over TLB1, invalidates all entries skipping the one which currently * maps this code. * * r29 current entry * r3-r5 scratched */ tlb1_inval_all_but_current: mfspr %r3, SPR_TLB1CFG /* Get number of entries */ andi. %r3, %r3, TLBCFG_NENTRY_MASK@l li %r4, 0 /* Start from Entry 0 */ 1: lis %r5, MAS0_TLBSEL1@h rlwimi %r5, %r4, 16, 10, 15 mtspr SPR_MAS0, %r5 isync tlbre mfspr %r5, SPR_MAS1 cmpw %r4, %r29 /* our current entry? */ beq 2f rlwinm %r5, %r5, 0, 2, 31 /* clear VALID and IPROT bits */ mtspr SPR_MAS1, %r5 isync tlbwe isync msync 2: addi %r4, %r4, 1 cmpw %r4, %r3 /* Check if this is the last entry */ bne 1b blr /* * MAS7 and MAS8 conditional zeroing. */ .globl zero_mas7 zero_mas7: mfpvr %r20 rlwinm %r20, %r20, 16, 16, 31 cmpli 0, 0, %r20, FSL_E500v1 beq 1f li %r20, 0 mtspr SPR_MAS7, %r20 isync 1: blr .globl zero_mas8 zero_mas8: mfpvr %r20 rlwinm %r20, %r20, 16, 16, 31 cmpli 0, 0, %r20, FSL_E500mc beq 1f cmpli 0, 0, %r20, FSL_E5500 beq 1f blr 1: li %r20, 0 mtspr SPR_MAS8, %r20 isync blr #endif #ifdef SMP .globl __boot_tlb1 /* * The __boot_tlb1 table is used to hold BSP TLB1 entries * marked with _TLB_ENTRY_SHARED flag during AP bootstrap. * The BSP fills in the table in tlb_ap_prep() function. Next, * AP loads its contents to TLB1 hardware in pmap_bootstrap_ap(). */ __boot_tlb1: .space TLB1_MAX_ENTRIES * TLB_ENTRY_SIZE __boot_page_padding: /* * Boot page needs to be exactly 4K, with the last word of this page * acting as the reset vector, so we need to stuff the remainder. * Upon release from holdoff CPU fetches the last word of the boot * page. */ .space 4092 - (__boot_page_padding - __boot_page) b __boot_page #endif /* SMP */ /************************************************************************/ /* locore subroutines */ /************************************************************************/ /* * Cache disable/enable/inval sequences according * to section 2.16 of E500CORE RM. */ ENTRY(dcache_inval) /* Invalidate d-cache */ mfspr %r3, SPR_L1CSR0 ori %r3, %r3, (L1CSR0_DCFI | L1CSR0_DCLFR)@l msync isync mtspr SPR_L1CSR0, %r3 isync 1: mfspr %r3, SPR_L1CSR0 andi. %r3, %r3, L1CSR0_DCFI bne 1b blr ENTRY(dcache_disable) /* Disable d-cache */ mfspr %r3, SPR_L1CSR0 li %r4, L1CSR0_DCE@l not %r4, %r4 and %r3, %r3, %r4 msync isync mtspr SPR_L1CSR0, %r3 isync blr ENTRY(dcache_enable) /* Enable d-cache */ mfspr %r3, SPR_L1CSR0 oris %r3, %r3, (L1CSR0_DCPE | L1CSR0_DCE)@h ori %r3, %r3, (L1CSR0_DCPE | L1CSR0_DCE)@l msync isync mtspr SPR_L1CSR0, %r3 isync blr ENTRY(icache_inval) /* Invalidate i-cache */ mfspr %r3, SPR_L1CSR1 ori %r3, %r3, (L1CSR1_ICFI | L1CSR1_ICLFR)@l isync mtspr SPR_L1CSR1, %r3 isync 1: mfspr %r3, SPR_L1CSR1 andi. %r3, %r3, L1CSR1_ICFI bne 1b blr ENTRY(icache_disable) /* Disable i-cache */ mfspr %r3, SPR_L1CSR1 li %r4, L1CSR1_ICE@l not %r4, %r4 and %r3, %r3, %r4 isync mtspr SPR_L1CSR1, %r3 isync blr ENTRY(icache_enable) /* Enable i-cache */ mfspr %r3, SPR_L1CSR1 oris %r3, %r3, (L1CSR1_ICPE | L1CSR1_ICE)@h ori %r3, %r3, (L1CSR1_ICPE | L1CSR1_ICE)@l isync mtspr SPR_L1CSR1, %r3 isync blr /* * L2 cache disable/enable/inval sequences for E500mc. */ ENTRY(l2cache_inval) mfspr %r3, SPR_L2CSR0 oris %r3, %r3, (L2CSR0_L2FI | L2CSR0_L2LFC)@h ori %r3, %r3, (L2CSR0_L2FI | L2CSR0_L2LFC)@l isync mtspr SPR_L2CSR0, %r3 isync 1: mfspr %r3, SPR_L2CSR0 andis. %r3, %r3, L2CSR0_L2FI@h bne 1b blr ENTRY(l2cache_enable) mfspr %r3, SPR_L2CSR0 oris %r3, %r3, (L2CSR0_L2E | L2CSR0_L2PE)@h isync mtspr SPR_L2CSR0, %r3 isync blr /* * Branch predictor setup. */ ENTRY(bpred_enable) mfspr %r3, SPR_BUCSR ori %r3, %r3, BUCSR_BBFI isync mtspr SPR_BUCSR, %r3 isync ori %r3, %r3, BUCSR_BPEN isync mtspr SPR_BUCSR, %r3 isync blr /* * XXX: This should be moved to a shared AIM/booke asm file, if one ever is * created. */ ENTRY(get_spr) mfspr %r3, 0 blr /************************************************************************/ /* Data section */ /************************************************************************/ .data .align 3 GLOBAL(__startkernel) ADDR(begin) GLOBAL(__endkernel) ADDR(end) .align 4 tmpstack: .space TMPSTACKSZ tmpstackbound: .space 10240 /* XXX: this really should not be necessary */ #ifdef __powerpc64__ TOC_ENTRY(tmpstack) TOC_ENTRY(bp_kernload) #endif /* * Compiled KERNBASE locations */ .globl kernbase .set kernbase, KERNBASE #include Index: projects/clang700-import/sys/powerpc/include/atomic.h =================================================================== --- projects/clang700-import/sys/powerpc/include/atomic.h (revision 341812) +++ projects/clang700-import/sys/powerpc/include/atomic.h (revision 341813) @@ -1,888 +1,891 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 Marcel Moolenaar * Copyright (c) 2001 Benno Rice * Copyright (c) 2001 David E. O'Brien * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_ATOMIC_H_ #define _MACHINE_ATOMIC_H_ #ifndef _SYS_CDEFS_H_ #error this file needs sys/cdefs.h as a prerequisite #endif #include /* * The __ATOMIC_REL/ACQ() macros provide memory barriers only in conjunction * with the atomic lXarx/stXcx. sequences below. They are not exposed outside * of this file. See also Appendix B.2 of Book II of the architecture manual. * * Note that not all Book-E processors accept the light-weight sync variant. * In particular, early models of E500 cores are known to wedge. Bank on all * 64-bit capable CPUs to accept lwsync properly and pressimize 32-bit CPUs * to use the heavier-weight sync. */ #ifdef __powerpc64__ #define mb() __asm __volatile("sync" : : : "memory") #define rmb() __asm __volatile("lwsync" : : : "memory") #define wmb() __asm __volatile("lwsync" : : : "memory") #define __ATOMIC_REL() __asm __volatile("lwsync" : : : "memory") #define __ATOMIC_ACQ() __asm __volatile("isync" : : : "memory") #else #define mb() __asm __volatile("sync" : : : "memory") #define rmb() __asm __volatile("sync" : : : "memory") #define wmb() __asm __volatile("sync" : : : "memory") #define __ATOMIC_REL() __asm __volatile("sync" : : : "memory") #define __ATOMIC_ACQ() __asm __volatile("isync" : : : "memory") #endif static __inline void powerpc_lwsync(void) { #ifdef __powerpc64__ __asm __volatile("lwsync" : : : "memory"); #else __asm __volatile("sync" : : : "memory"); #endif } /* * atomic_add(p, v) * { *p += v; } */ #define __atomic_add_int(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " add %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_add_int */ #ifdef __powerpc64__ #define __atomic_add_long(p, v, t) \ __asm __volatile( \ "1: ldarx %0, 0, %2\n" \ " add %0, %3, %0\n" \ " stdcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_add_long */ #else #define __atomic_add_long(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " add %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_add_long */ #endif #define _ATOMIC_ADD(type) \ static __inline void \ atomic_add_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_add_##type(p, v, t); \ } \ \ static __inline void \ atomic_add_acq_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_add_##type(p, v, t); \ __ATOMIC_ACQ(); \ } \ \ static __inline void \ atomic_add_rel_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __ATOMIC_REL(); \ __atomic_add_##type(p, v, t); \ } \ /* _ATOMIC_ADD */ _ATOMIC_ADD(int) _ATOMIC_ADD(long) #define atomic_add_32 atomic_add_int #define atomic_add_acq_32 atomic_add_acq_int #define atomic_add_rel_32 atomic_add_rel_int #ifdef __powerpc64__ #define atomic_add_64 atomic_add_long #define atomic_add_acq_64 atomic_add_acq_long #define atomic_add_rel_64 atomic_add_rel_long #define atomic_add_ptr atomic_add_long #define atomic_add_acq_ptr atomic_add_acq_long #define atomic_add_rel_ptr atomic_add_rel_long #else #define atomic_add_ptr atomic_add_int #define atomic_add_acq_ptr atomic_add_acq_int #define atomic_add_rel_ptr atomic_add_rel_int #endif #undef _ATOMIC_ADD #undef __atomic_add_long #undef __atomic_add_int /* * atomic_clear(p, v) * { *p &= ~v; } */ #define __atomic_clear_int(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " andc %0, %0, %3\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_clear_int */ #ifdef __powerpc64__ #define __atomic_clear_long(p, v, t) \ __asm __volatile( \ "1: ldarx %0, 0, %2\n" \ " andc %0, %0, %3\n" \ " stdcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_clear_long */ #else #define __atomic_clear_long(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " andc %0, %0, %3\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_clear_long */ #endif #define _ATOMIC_CLEAR(type) \ static __inline void \ atomic_clear_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_clear_##type(p, v, t); \ } \ \ static __inline void \ atomic_clear_acq_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_clear_##type(p, v, t); \ __ATOMIC_ACQ(); \ } \ \ static __inline void \ atomic_clear_rel_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __ATOMIC_REL(); \ __atomic_clear_##type(p, v, t); \ } \ /* _ATOMIC_CLEAR */ _ATOMIC_CLEAR(int) _ATOMIC_CLEAR(long) #define atomic_clear_32 atomic_clear_int #define atomic_clear_acq_32 atomic_clear_acq_int #define atomic_clear_rel_32 atomic_clear_rel_int #ifdef __powerpc64__ #define atomic_clear_64 atomic_clear_long #define atomic_clear_acq_64 atomic_clear_acq_long #define atomic_clear_rel_64 atomic_clear_rel_long #define atomic_clear_ptr atomic_clear_long #define atomic_clear_acq_ptr atomic_clear_acq_long #define atomic_clear_rel_ptr atomic_clear_rel_long #else #define atomic_clear_ptr atomic_clear_int #define atomic_clear_acq_ptr atomic_clear_acq_int #define atomic_clear_rel_ptr atomic_clear_rel_int #endif #undef _ATOMIC_CLEAR #undef __atomic_clear_long #undef __atomic_clear_int /* * atomic_cmpset(p, o, n) */ /* TODO -- see below */ /* * atomic_load_acq(p) */ /* TODO -- see below */ /* * atomic_readandclear(p) */ /* TODO -- see below */ /* * atomic_set(p, v) * { *p |= v; } */ #define __atomic_set_int(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " or %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_set_int */ #ifdef __powerpc64__ #define __atomic_set_long(p, v, t) \ __asm __volatile( \ "1: ldarx %0, 0, %2\n" \ " or %0, %3, %0\n" \ " stdcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_set_long */ #else #define __atomic_set_long(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " or %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_set_long */ #endif #define _ATOMIC_SET(type) \ static __inline void \ atomic_set_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_set_##type(p, v, t); \ } \ \ static __inline void \ atomic_set_acq_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_set_##type(p, v, t); \ __ATOMIC_ACQ(); \ } \ \ static __inline void \ atomic_set_rel_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __ATOMIC_REL(); \ __atomic_set_##type(p, v, t); \ } \ /* _ATOMIC_SET */ _ATOMIC_SET(int) _ATOMIC_SET(long) #define atomic_set_32 atomic_set_int #define atomic_set_acq_32 atomic_set_acq_int #define atomic_set_rel_32 atomic_set_rel_int #ifdef __powerpc64__ #define atomic_set_64 atomic_set_long #define atomic_set_acq_64 atomic_set_acq_long #define atomic_set_rel_64 atomic_set_rel_long #define atomic_set_ptr atomic_set_long #define atomic_set_acq_ptr atomic_set_acq_long #define atomic_set_rel_ptr atomic_set_rel_long #else #define atomic_set_ptr atomic_set_int #define atomic_set_acq_ptr atomic_set_acq_int #define atomic_set_rel_ptr atomic_set_rel_int #endif #undef _ATOMIC_SET #undef __atomic_set_long #undef __atomic_set_int /* * atomic_subtract(p, v) * { *p -= v; } */ #define __atomic_subtract_int(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " subf %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_subtract_int */ #ifdef __powerpc64__ #define __atomic_subtract_long(p, v, t) \ __asm __volatile( \ "1: ldarx %0, 0, %2\n" \ " subf %0, %3, %0\n" \ " stdcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_subtract_long */ #else #define __atomic_subtract_long(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " subf %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_subtract_long */ #endif #define _ATOMIC_SUBTRACT(type) \ static __inline void \ atomic_subtract_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_subtract_##type(p, v, t); \ } \ \ static __inline void \ atomic_subtract_acq_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_subtract_##type(p, v, t); \ __ATOMIC_ACQ(); \ } \ \ static __inline void \ atomic_subtract_rel_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __ATOMIC_REL(); \ __atomic_subtract_##type(p, v, t); \ } \ /* _ATOMIC_SUBTRACT */ _ATOMIC_SUBTRACT(int) _ATOMIC_SUBTRACT(long) #define atomic_subtract_32 atomic_subtract_int #define atomic_subtract_acq_32 atomic_subtract_acq_int #define atomic_subtract_rel_32 atomic_subtract_rel_int #ifdef __powerpc64__ #define atomic_subtract_64 atomic_subtract_long #define atomic_subtract_acq_64 atomic_subract_acq_long #define atomic_subtract_rel_64 atomic_subtract_rel_long #define atomic_subtract_ptr atomic_subtract_long #define atomic_subtract_acq_ptr atomic_subtract_acq_long #define atomic_subtract_rel_ptr atomic_subtract_rel_long #else #define atomic_subtract_ptr atomic_subtract_int #define atomic_subtract_acq_ptr atomic_subtract_acq_int #define atomic_subtract_rel_ptr atomic_subtract_rel_int #endif #undef _ATOMIC_SUBTRACT #undef __atomic_subtract_long #undef __atomic_subtract_int /* * atomic_store_rel(p, v) */ /* TODO -- see below */ /* * Old/original implementations that still need revisiting. */ static __inline u_int atomic_readandclear_int(volatile u_int *addr) { u_int result,temp; __asm __volatile ( "\tsync\n" /* drain writes */ "1:\tlwarx %0, 0, %3\n\t" /* load old value */ "li %1, 0\n\t" /* load new value */ "stwcx. %1, 0, %3\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ : "=&r"(result), "=&r"(temp), "=m" (*addr) : "r" (addr), "m" (*addr) : "cr0", "memory"); return (result); } #ifdef __powerpc64__ static __inline u_long atomic_readandclear_long(volatile u_long *addr) { u_long result,temp; __asm __volatile ( "\tsync\n" /* drain writes */ "1:\tldarx %0, 0, %3\n\t" /* load old value */ "li %1, 0\n\t" /* load new value */ "stdcx. %1, 0, %3\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ : "=&r"(result), "=&r"(temp), "=m" (*addr) : "r" (addr), "m" (*addr) : "cr0", "memory"); return (result); } #endif #define atomic_readandclear_32 atomic_readandclear_int #ifdef __powerpc64__ #define atomic_readandclear_64 atomic_readandclear_long #define atomic_readandclear_ptr atomic_readandclear_long #else static __inline u_long atomic_readandclear_long(volatile u_long *addr) { return ((u_long)atomic_readandclear_int((volatile u_int *)addr)); } #define atomic_readandclear_ptr atomic_readandclear_int #endif /* * We assume that a = b will do atomic loads and stores. */ #define ATOMIC_STORE_LOAD(TYPE) \ static __inline u_##TYPE \ atomic_load_acq_##TYPE(volatile u_##TYPE *p) \ { \ u_##TYPE v; \ \ v = *p; \ powerpc_lwsync(); \ return (v); \ } \ \ static __inline void \ atomic_store_rel_##TYPE(volatile u_##TYPE *p, u_##TYPE v) \ { \ \ powerpc_lwsync(); \ *p = v; \ } ATOMIC_STORE_LOAD(int) #define atomic_load_acq_32 atomic_load_acq_int #define atomic_store_rel_32 atomic_store_rel_int #ifdef __powerpc64__ ATOMIC_STORE_LOAD(long) #define atomic_load_acq_64 atomic_load_acq_long #define atomic_store_rel_64 atomic_store_rel_long #define atomic_load_acq_ptr atomic_load_acq_long #define atomic_store_rel_ptr atomic_store_rel_long #else static __inline u_long atomic_load_acq_long(volatile u_long *addr) { return ((u_long)atomic_load_acq_int((volatile u_int *)addr)); } static __inline void atomic_store_rel_long(volatile u_long *addr, u_long val) { atomic_store_rel_int((volatile u_int *)addr, (u_int)val); } #define atomic_load_acq_ptr atomic_load_acq_int #define atomic_store_rel_ptr atomic_store_rel_int #endif #undef ATOMIC_STORE_LOAD /* * Atomically compare the value stored at *p with cmpval and if the * two values are equal, update the value of *p with newval. Returns * zero if the compare failed, nonzero otherwise. */ static __inline int atomic_cmpset_int(volatile u_int* p, u_int cmpval, u_int newval) { int ret; __asm __volatile ( "1:\tlwarx %0, 0, %2\n\t" /* load old value */ "cmplw %3, %0\n\t" /* compare */ "bne 2f\n\t" /* exit if not equal */ "stwcx. %4, 0, %2\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 3f\n\t" /* we've succeeded */ "2:\n\t" "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ "li %0, 0\n\t" /* failure - retval = 0 */ "3:\n\t" : "=&r" (ret), "=m" (*p) : "r" (p), "r" (cmpval), "r" (newval), "m" (*p) : "cr0", "memory"); return (ret); } static __inline int atomic_cmpset_long(volatile u_long* p, u_long cmpval, u_long newval) { int ret; __asm __volatile ( #ifdef __powerpc64__ "1:\tldarx %0, 0, %2\n\t" /* load old value */ "cmpld %3, %0\n\t" /* compare */ "bne 2f\n\t" /* exit if not equal */ "stdcx. %4, 0, %2\n\t" /* attempt to store */ #else "1:\tlwarx %0, 0, %2\n\t" /* load old value */ "cmplw %3, %0\n\t" /* compare */ "bne 2f\n\t" /* exit if not equal */ "stwcx. %4, 0, %2\n\t" /* attempt to store */ #endif "bne- 1b\n\t" /* spin if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 3f\n\t" /* we've succeeded */ "2:\n\t" #ifdef __powerpc64__ "stdcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ #else "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ #endif "li %0, 0\n\t" /* failure - retval = 0 */ "3:\n\t" : "=&r" (ret), "=m" (*p) : "r" (p), "r" (cmpval), "r" (newval), "m" (*p) : "cr0", "memory"); return (ret); } static __inline int atomic_cmpset_acq_int(volatile u_int *p, u_int cmpval, u_int newval) { int retval; retval = atomic_cmpset_int(p, cmpval, newval); __ATOMIC_ACQ(); return (retval); } static __inline int atomic_cmpset_rel_int(volatile u_int *p, u_int cmpval, u_int newval) { __ATOMIC_REL(); return (atomic_cmpset_int(p, cmpval, newval)); } static __inline int atomic_cmpset_acq_long(volatile u_long *p, u_long cmpval, u_long newval) { u_long retval; retval = atomic_cmpset_long(p, cmpval, newval); __ATOMIC_ACQ(); return (retval); } static __inline int atomic_cmpset_rel_long(volatile u_long *p, u_long cmpval, u_long newval) { __ATOMIC_REL(); return (atomic_cmpset_long(p, cmpval, newval)); } #define atomic_cmpset_32 atomic_cmpset_int #define atomic_cmpset_acq_32 atomic_cmpset_acq_int #define atomic_cmpset_rel_32 atomic_cmpset_rel_int #ifdef __powerpc64__ #define atomic_cmpset_64 atomic_cmpset_long #define atomic_cmpset_acq_64 atomic_cmpset_acq_long #define atomic_cmpset_rel_64 atomic_cmpset_rel_long #define atomic_cmpset_ptr atomic_cmpset_long #define atomic_cmpset_acq_ptr atomic_cmpset_acq_long #define atomic_cmpset_rel_ptr atomic_cmpset_rel_long #else #define atomic_cmpset_ptr atomic_cmpset_int #define atomic_cmpset_acq_ptr atomic_cmpset_acq_int #define atomic_cmpset_rel_ptr atomic_cmpset_rel_int #endif /* * Atomically compare the value stored at *p with *cmpval and if the * two values are equal, update the value of *p with newval. Returns * zero if the compare failed and sets *cmpval to the read value from *p, * nonzero otherwise. */ static __inline int atomic_fcmpset_int(volatile u_int *p, u_int *cmpval, u_int newval) { int ret; __asm __volatile ( "lwarx %0, 0, %3\n\t" /* load old value */ "cmplw %4, %0\n\t" /* compare */ "bne 1f\n\t" /* exit if not equal */ "stwcx. %5, 0, %3\n\t" /* attempt to store */ "bne- 1f\n\t" /* exit if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 2f\n\t" /* we've succeeded */ "1:\n\t" "stwcx. %0, 0, %3\n\t" /* clear reservation (74xx) */ "stwx %0, 0, %7\n\t" "li %0, 0\n\t" /* failure - retval = 0 */ "2:\n\t" : "=&r" (ret), "=m" (*p), "=m" (*cmpval) : "r" (p), "r" (*cmpval), "r" (newval), "m" (*p), "r"(cmpval) : "cr0", "memory"); return (ret); } static __inline int atomic_fcmpset_long(volatile u_long *p, u_long *cmpval, u_long newval) { int ret; __asm __volatile ( #ifdef __powerpc64__ "ldarx %0, 0, %3\n\t" /* load old value */ "cmpld %4, %0\n\t" /* compare */ "bne 1f\n\t" /* exit if not equal */ "stdcx. %5, 0, %3\n\t" /* attempt to store */ #else "lwarx %0, 0, %3\n\t" /* load old value */ "cmplw %4, %0\n\t" /* compare */ "bne 1f\n\t" /* exit if not equal */ "stwcx. %5, 0, %3\n\t" /* attempt to store */ #endif "bne- 1f\n\t" /* exit if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 2f\n\t" /* we've succeeded */ "1:\n\t" #ifdef __powerpc64__ "stdcx. %0, 0, %3\n\t" /* clear reservation (74xx) */ "stdx %0, 0, %7\n\t" #else "stwcx. %0, 0, %3\n\t" /* clear reservation (74xx) */ "stwx %0, 0, %7\n\t" #endif "li %0, 0\n\t" /* failure - retval = 0 */ "2:\n\t" : "=&r" (ret), "=m" (*p), "=m" (*cmpval) : "r" (p), "r" (*cmpval), "r" (newval), "m" (*p), "r"(cmpval) : "cr0", "memory"); return (ret); } static __inline int atomic_fcmpset_acq_int(volatile u_int *p, u_int *cmpval, u_int newval) { int retval; retval = atomic_fcmpset_int(p, cmpval, newval); __ATOMIC_ACQ(); return (retval); } static __inline int atomic_fcmpset_rel_int(volatile u_int *p, u_int *cmpval, u_int newval) { __ATOMIC_REL(); return (atomic_fcmpset_int(p, cmpval, newval)); } static __inline int atomic_fcmpset_acq_long(volatile u_long *p, u_long *cmpval, u_long newval) { u_long retval; retval = atomic_fcmpset_long(p, cmpval, newval); __ATOMIC_ACQ(); return (retval); } static __inline int atomic_fcmpset_rel_long(volatile u_long *p, u_long *cmpval, u_long newval) { __ATOMIC_REL(); return (atomic_fcmpset_long(p, cmpval, newval)); } #define atomic_fcmpset_32 atomic_fcmpset_int #define atomic_fcmpset_acq_32 atomic_fcmpset_acq_int #define atomic_fcmpset_rel_32 atomic_fcmpset_rel_int #ifdef __powerpc64__ #define atomic_fcmpset_64 atomic_fcmpset_long #define atomic_fcmpset_acq_64 atomic_fcmpset_acq_long #define atomic_fcmpset_rel_64 atomic_fcmpset_rel_long #define atomic_fcmpset_ptr atomic_fcmpset_long #define atomic_fcmpset_acq_ptr atomic_fcmpset_acq_long #define atomic_fcmpset_rel_ptr atomic_fcmpset_rel_long #else #define atomic_fcmpset_ptr atomic_fcmpset_int #define atomic_fcmpset_acq_ptr atomic_fcmpset_acq_int #define atomic_fcmpset_rel_ptr atomic_fcmpset_rel_int #endif static __inline u_int atomic_fetchadd_int(volatile u_int *p, u_int v) { u_int value; do { value = *p; } while (!atomic_cmpset_int(p, value, value + v)); return (value); } static __inline u_long atomic_fetchadd_long(volatile u_long *p, u_long v) { u_long value; do { value = *p; } while (!atomic_cmpset_long(p, value, value + v)); return (value); } static __inline u_int atomic_swap_32(volatile u_int *p, u_int v) { u_int prev; __asm __volatile( "1: lwarx %0,0,%2\n" " stwcx. %3,0,%2\n" " bne- 1b\n" : "=&r" (prev), "+m" (*(volatile u_int *)p) : "r" (p), "r" (v) : "cr0", "memory"); return (prev); } #ifdef __powerpc64__ static __inline u_long atomic_swap_64(volatile u_long *p, u_long v) { u_long prev; __asm __volatile( "1: ldarx %0,0,%2\n" " stdcx. %3,0,%2\n" " bne- 1b\n" : "=&r" (prev), "+m" (*(volatile u_long *)p) : "r" (p), "r" (v) : "cr0", "memory"); return (prev); } #endif #define atomic_fetchadd_32 atomic_fetchadd_int #define atomic_swap_int atomic_swap_32 #ifdef __powerpc64__ #define atomic_fetchadd_64 atomic_fetchadd_long #define atomic_swap_long atomic_swap_64 #define atomic_swap_ptr atomic_swap_64 +#else +#define atomic_swap_long(p,v) atomic_swap_32((volatile u_int *)(p), v) +#define atomic_swap_ptr(p,v) atomic_swap_32((volatile u_int *)(p), v) #endif #undef __ATOMIC_REL #undef __ATOMIC_ACQ static __inline void atomic_thread_fence_acq(void) { powerpc_lwsync(); } static __inline void atomic_thread_fence_rel(void) { powerpc_lwsync(); } static __inline void atomic_thread_fence_acq_rel(void) { powerpc_lwsync(); } static __inline void atomic_thread_fence_seq_cst(void) { __asm __volatile("sync" : : : "memory"); } #endif /* ! _MACHINE_ATOMIC_H_ */ Index: projects/clang700-import/sys/powerpc/include/trap.h =================================================================== --- projects/clang700-import/sys/powerpc/include/trap.h (revision 341812) +++ projects/clang700-import/sys/powerpc/include/trap.h (revision 341813) @@ -1,157 +1,160 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: trap.h,v 1.7 2002/02/22 13:51:40 kleink Exp $ * $FreeBSD$ */ #ifndef _POWERPC_TRAP_H_ #define _POWERPC_TRAP_H_ #define EXC_RSVD 0x0000 /* Reserved */ #define EXC_RST 0x0100 /* Reset; all but IBM4xx */ #define EXC_MCHK 0x0200 /* Machine Check */ #define EXC_DSI 0x0300 /* Data Storage Interrupt */ #define EXC_DSE 0x0380 /* Data Segment Interrupt */ #define EXC_ISI 0x0400 /* Instruction Storage Interrupt */ #define EXC_ISE 0x0480 /* Instruction Segment Interrupt */ #define EXC_EXI 0x0500 /* External Interrupt */ #define EXC_ALI 0x0600 /* Alignment Interrupt */ #define EXC_PGM 0x0700 /* Program Interrupt */ #define EXC_FPU 0x0800 /* Floating-point Unavailable */ #define EXC_DECR 0x0900 /* Decrementer Interrupt */ #define EXC_SC 0x0c00 /* System Call */ #define EXC_TRC 0x0d00 /* Trace */ #define EXC_FPA 0x0e00 /* Floating-point Assist */ /* The following is only available on the 601: */ #define EXC_RUNMODETRC 0x2000 /* Run Mode/Trace Exception */ /* The following are only available on 970(G5): */ #define EXC_VECAST_G5 0x1700 /* AltiVec Assist */ /* The following are only available on 7400(G4): */ #define EXC_VEC 0x0f20 /* AltiVec Unavailable */ #define EXC_VECAST_G4 0x1600 /* AltiVec Assist */ /* The following are only available on 604/750/7400: */ #define EXC_PERF 0x0f00 /* Performance Monitoring */ #define EXC_BPT 0x1300 /* Instruction Breakpoint */ #define EXC_SMI 0x1400 /* System Managment Interrupt */ /* The following are only available on 750/7400: */ #define EXC_THRM 0x1700 /* Thermal Management Interrupt */ /* And these are only on the 603: */ #define EXC_IMISS 0x1000 /* Instruction translation miss */ #define EXC_DLMISS 0x1100 /* Data load translation miss */ #define EXC_DSMISS 0x1200 /* Data store translation miss */ /* Power ISA 2.06+: */ #define EXC_HDSI 0x0e00 /* Hypervisor Data Storage */ #define EXC_HISI 0x0e20 /* Hypervisor Instruction Storage */ #define EXC_HEA 0x0e40 /* Hypervisor Emulation Assistance */ #define EXC_HMI 0x0e60 /* Hypervisor Maintenance */ #define EXC_VSX 0x0f40 /* VSX Unavailable */ /* Power ISA 2.07+: */ #define EXC_FAC 0x0f60 /* Facility Unavailable */ #define EXC_HFAC 0x0f80 /* Hypervisor Facility Unavailable */ /* Power ISA 3.0+: */ #define EXC_HVI 0x0ea0 /* Hypervisor Virtualization */ /* The following are available on 4xx and 85xx */ #define EXC_CRIT 0x0100 /* Critical Input Interrupt */ #define EXC_PIT 0x1000 /* Programmable Interval Timer */ #define EXC_FIT 0x1010 /* Fixed Interval Timer */ #define EXC_WDOG 0x1020 /* Watchdog Timer */ #define EXC_DTMISS 0x1100 /* Data TLB Miss */ #define EXC_ITMISS 0x1200 /* Instruction TLB Miss */ #define EXC_APU 0x1300 /* Auxiliary Processing Unit */ #define EXC_DEBUG 0x2f10 /* Debug trap */ #define EXC_VECAST_E 0x2f20 /* Altivec Assist (Book-E) */ #define EXC_SPFPD 0x2f30 /* SPE Floating-point Data */ #define EXC_SPFPR 0x2f40 /* SPE Floating-point Round */ +/* POWER8 */ +#define EXC_SOFT_PATCH 0x1500 /* POWER8 Soft Patch Exception */ + #define EXC_LAST 0x2f00 /* Last possible exception vector */ #define EXC_AST 0x3000 /* Fake AST vector */ /* Trap was in user mode */ #define EXC_USER 0x10000 /* * EXC_ALI sets bits in the DSISR and DAR to provide enough * information to recover from the unaligned access without needing to * parse the offending instruction. This includes certain bits of the * opcode, and information about what registers are used. The opcode * indicator values below come from Appendix F of Book III of "The * PowerPC Architecture". */ #define EXC_ALI_OPCODE_INDICATOR(dsisr) ((dsisr >> 10) & 0x7f) #define EXC_ALI_LFD 0x09 #define EXC_ALI_STFD 0x0b /* Macros to extract register information */ #define EXC_ALI_RST(dsisr) ((dsisr >> 5) & 0x1f) /* source or target */ #define EXC_ALI_RA(dsisr) (dsisr & 0x1f) #define EXC_ALI_SPE_REG(instr) ((instr >> 21) & 0x1f) /* * SRR1 bits for program exception traps. These identify what caused * the program exception. See section 6.5.9 of the Power ISA Version * 2.05. */ #define EXC_PGM_FPENABLED (1UL << 20) #define EXC_PGM_ILLEGAL (1UL << 19) #define EXC_PGM_PRIV (1UL << 18) #define EXC_PGM_TRAP (1UL << 17) /* DTrace trap opcode. */ #define EXC_DTRACE 0x7ffff808 /* Magic pointer to store TOC base and other info for trap handlers on ppc64 */ #define TRAP_GENTRAP 0x1f0 #define TRAP_TOCBASE 0x1f8 #ifndef LOCORE struct trapframe; struct pcb; void trap(struct trapframe *); int ppc_instr_emulate(struct trapframe *, struct pcb *); #endif #endif /* _POWERPC_TRAP_H_ */ Index: projects/clang700-import/sys/powerpc/mpc85xx/platform_mpc85xx.c =================================================================== --- projects/clang700-import/sys/powerpc/mpc85xx/platform_mpc85xx.c (revision 341812) +++ projects/clang700-import/sys/powerpc/mpc85xx/platform_mpc85xx.c (revision 341813) @@ -1,535 +1,535 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008-2012 Semihalf. * 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 "opt_platform.h" #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 "platform_if.h" #ifdef SMP extern void *ap_pcpu; extern vm_paddr_t kernload; /* Kernel physical load address */ extern uint8_t __boot_page[]; /* Boot page body */ extern uint32_t bp_kernload; extern vm_offset_t __startkernel; struct cpu_release { uint32_t entry_h; uint32_t entry_l; uint32_t r3_h; uint32_t r3_l; uint32_t reserved; uint32_t pir; }; #endif extern uint32_t *bootinfo; vm_paddr_t ccsrbar_pa; vm_offset_t ccsrbar_va; vm_size_t ccsrbar_size; static int cpu, maxcpu; static int mpc85xx_probe(platform_t); static void mpc85xx_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static u_long mpc85xx_timebase_freq(platform_t, struct cpuref *cpuref); static int mpc85xx_smp_first_cpu(platform_t, struct cpuref *cpuref); static int mpc85xx_smp_next_cpu(platform_t, struct cpuref *cpuref); static int mpc85xx_smp_get_bsp(platform_t, struct cpuref *cpuref); static int mpc85xx_smp_start_cpu(platform_t, struct pcpu *cpu); static void mpc85xx_smp_timebase_sync(platform_t, u_long tb, int ap); static void mpc85xx_reset(platform_t); static platform_method_t mpc85xx_methods[] = { PLATFORMMETHOD(platform_probe, mpc85xx_probe), PLATFORMMETHOD(platform_attach, mpc85xx_attach), PLATFORMMETHOD(platform_mem_regions, mpc85xx_mem_regions), PLATFORMMETHOD(platform_timebase_freq, mpc85xx_timebase_freq), PLATFORMMETHOD(platform_smp_first_cpu, mpc85xx_smp_first_cpu), PLATFORMMETHOD(platform_smp_next_cpu, mpc85xx_smp_next_cpu), PLATFORMMETHOD(platform_smp_get_bsp, mpc85xx_smp_get_bsp), PLATFORMMETHOD(platform_smp_start_cpu, mpc85xx_smp_start_cpu), PLATFORMMETHOD(platform_smp_timebase_sync, mpc85xx_smp_timebase_sync), PLATFORMMETHOD(platform_reset, mpc85xx_reset), PLATFORMMETHOD_END }; DEFINE_CLASS_0(mpc85xx, mpc85xx_platform, mpc85xx_methods, 0); PLATFORM_DEF(mpc85xx_platform); static int mpc85xx_probe(platform_t plat) { u_int pvr = (mfpvr() >> 16) & 0xFFFF; switch (pvr) { case FSL_E500v1: case FSL_E500v2: case FSL_E500mc: case FSL_E5500: case FSL_E6500: return (BUS_PROBE_DEFAULT); } return (ENXIO); } int mpc85xx_attach(platform_t plat) { phandle_t cpus, child, ccsr; const char *soc_name_guesses[] = {"/soc", "soc", NULL}; const char **name; pcell_t ranges[6], acells, pacells, scells; uint64_t ccsrbar, ccsrsize; int i; if ((cpus = OF_finddevice("/cpus")) != -1) { for (maxcpu = 0, child = OF_child(cpus); child != 0; child = OF_peer(child), maxcpu++) ; } else maxcpu = 1; /* * Locate CCSR region. Irritatingly, there is no way to find it * unless you already know where it is. Try to infer its location * from the device tree. */ ccsr = -1; for (name = soc_name_guesses; *name != NULL && ccsr == -1; name++) ccsr = OF_finddevice(*name); if (ccsr == -1) { char type[64]; /* That didn't work. Search for devices of type "soc" */ child = OF_child(OF_peer(0)); for (OF_child(child); child != 0; child = OF_peer(child)) { if (OF_getprop(child, "device_type", type, sizeof(type)) <= 0) continue; if (strcmp(type, "soc") == 0) { ccsr = child; break; } } } if (ccsr == -1) panic("Could not locate CCSR window!"); OF_getprop(ccsr, "#size-cells", &scells, sizeof(scells)); OF_getprop(ccsr, "#address-cells", &acells, sizeof(acells)); OF_searchprop(OF_parent(ccsr), "#address-cells", &pacells, sizeof(pacells)); OF_getprop(ccsr, "ranges", ranges, sizeof(ranges)); ccsrbar = ccsrsize = 0; for (i = acells; i < acells + pacells; i++) { ccsrbar <<= 32; ccsrbar |= ranges[i]; } for (i = acells + pacells; i < acells + pacells + scells; i++) { ccsrsize <<= 32; ccsrsize |= ranges[i]; } ccsrbar_va = pmap_early_io_map(ccsrbar, ccsrsize); ccsrbar_pa = ccsrbar; ccsrbar_size = ccsrsize; mpc85xx_enable_l3_cache(); return (0); } void mpc85xx_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz) { ofw_mem_regions(phys, physsz, avail, availsz); } static u_long mpc85xx_timebase_freq(platform_t plat, struct cpuref *cpuref) { u_long ticks; phandle_t cpus, child; pcell_t freq; if (bootinfo != NULL) { if (bootinfo[0] == 1) { /* Backward compatibility. See 8-STABLE. */ ticks = bootinfo[3] >> 3; } else { /* Compatibility with Juniper's loader. */ ticks = bootinfo[5] >> 3; } } else ticks = 0; if ((cpus = OF_finddevice("/cpus")) == -1) goto out; if ((child = OF_child(cpus)) == 0) goto out; switch (OF_getproplen(child, "timebase-frequency")) { case 4: { uint32_t tbase; OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase)); ticks = tbase; return (ticks); } case 8: { uint64_t tbase; OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase)); ticks = tbase; return (ticks); } default: break; } freq = 0; if (OF_getprop(child, "bus-frequency", (void *)&freq, sizeof(freq)) <= 0) goto out; if (freq == 0) goto out; /* * Time Base and Decrementer are updated every 8 CCB bus clocks. * HID0[SEL_TBCLK] = 0 */ if (mpc85xx_is_qoriq()) ticks = freq / 32; else ticks = freq / 8; out: if (ticks <= 0) panic("Unable to determine timebase frequency!"); return (ticks); } static int mpc85xx_smp_first_cpu(platform_t plat, struct cpuref *cpuref) { cpu = 0; cpuref->cr_cpuid = cpu; cpuref->cr_hwref = cpuref->cr_cpuid; if (bootverbose) printf("powerpc_smp_first_cpu: cpuid %d\n", cpuref->cr_cpuid); cpu++; return (0); } static int mpc85xx_smp_next_cpu(platform_t plat, struct cpuref *cpuref) { if (cpu >= maxcpu) return (ENOENT); cpuref->cr_cpuid = cpu++; cpuref->cr_hwref = cpuref->cr_cpuid; if (bootverbose) printf("powerpc_smp_next_cpu: cpuid %d\n", cpuref->cr_cpuid); return (0); } static int mpc85xx_smp_get_bsp(platform_t plat, struct cpuref *cpuref) { cpuref->cr_cpuid = mfspr(SPR_PIR); cpuref->cr_hwref = cpuref->cr_cpuid; return (0); } #ifdef SMP static int mpc85xx_smp_start_cpu_epapr(platform_t plat, struct pcpu *pc) { vm_paddr_t rel_pa, bptr; volatile struct cpu_release *rel; vm_offset_t rel_va, rel_page; phandle_t node; int i; /* If we're calling this, the node already exists. */ node = OF_finddevice("/cpus"); for (i = 0, node = OF_child(node); i < pc->pc_cpuid; i++, node = OF_peer(node)) ; if (OF_getencprop(node, "cpu-release-addr", (pcell_t *)&rel_pa, sizeof(rel_pa)) == -1) { return (ENOENT); } rel_page = kva_alloc(PAGE_SIZE); if (rel_page == 0) return (ENOMEM); critical_enter(); rel_va = rel_page + (rel_pa & PAGE_MASK); pmap_kenter(rel_page, rel_pa & ~PAGE_MASK); rel = (struct cpu_release *)rel_va; - bptr = ((vm_paddr_t)(uintptr_t)__boot_page - __startkernel) + kernload; + bptr = pmap_kextract((uintptr_t)__boot_page); cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel)); rel->pir = pc->pc_cpuid; __asm __volatile("sync"); rel->entry_h = (bptr >> 32); rel->entry_l = bptr; __asm __volatile("sync"); cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel)); if (bootverbose) printf("Waking up CPU %d via CPU release page %p\n", pc->pc_cpuid, rel); critical_exit(); pmap_kremove(rel_page); kva_free(rel_page, PAGE_SIZE); return (0); } #endif static int mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc) { #ifdef SMP vm_paddr_t bptr; uint32_t reg; int timeout; uintptr_t brr; int cpuid; int epapr_boot = 0; uint32_t tgt; if (mpc85xx_is_qoriq()) { reg = ccsr_read4(OCP85XX_COREDISR); cpuid = pc->pc_cpuid; if ((reg & (1 << cpuid)) != 0) { printf("%s: CPU %d is disabled!\n", __func__, pc->pc_cpuid); return (-1); } brr = OCP85XX_BRR; } else { brr = OCP85XX_EEBPCR; cpuid = pc->pc_cpuid + 24; } bp_kernload = kernload; /* * bp_kernload is in the boot page. Sync the cache because ePAPR * booting has the other core(s) already running. */ cpu_flush_dcache(&bp_kernload, sizeof(bp_kernload)); ap_pcpu = pc; __asm __volatile("msync; isync"); /* First try the ePAPR way. */ if (mpc85xx_smp_start_cpu_epapr(plat, pc) == 0) { epapr_boot = 1; goto spin_wait; } reg = ccsr_read4(brr); if ((reg & (1 << cpuid)) != 0) { printf("SMP: CPU %d already out of hold-off state!\n", pc->pc_cpuid); return (ENXIO); } /* Flush caches to have our changes hit DRAM. */ cpu_flush_dcache(__boot_page, 4096); - bptr = ((vm_paddr_t)(uintptr_t)__boot_page - __startkernel) + kernload; + bptr = pmap_kextract((uintptr_t)__boot_page); KASSERT((bptr & 0xfff) == 0, ("%s: boot page is not aligned (%#jx)", __func__, (uintmax_t)bptr)); if (mpc85xx_is_qoriq()) { /* * Read DDR controller configuration to select proper BPTR target ID. * * On P5020 bit 29 of DDR1_CS0_CONFIG enables DDR controllers * interleaving. If this bit is set, we have to use * OCP85XX_TGTIF_RAM_INTL as BPTR target ID. On other QorIQ DPAA SoCs, * this bit is reserved and always 0. */ reg = ccsr_read4(OCP85XX_DDR1_CS0_CONFIG); if (reg & (1 << 29)) tgt = OCP85XX_TGTIF_RAM_INTL; else tgt = OCP85XX_TGTIF_RAM1; /* * Set BSTR to the physical address of the boot page */ ccsr_write4(OCP85XX_BSTRH, bptr >> 32); ccsr_write4(OCP85XX_BSTRL, bptr); ccsr_write4(OCP85XX_BSTAR, OCP85XX_ENA_MASK | (tgt << OCP85XX_TRGT_SHIFT_QORIQ) | (ffsl(PAGE_SIZE) - 2)); /* Read back OCP85XX_BSTAR to synchronize write */ ccsr_read4(OCP85XX_BSTAR); /* * Enable and configure time base on new CPU. */ /* Set TB clock source to platform clock / 32 */ reg = ccsr_read4(CCSR_CTBCKSELR); ccsr_write4(CCSR_CTBCKSELR, reg & ~(1 << pc->pc_cpuid)); /* Enable TB */ reg = ccsr_read4(CCSR_CTBENR); ccsr_write4(CCSR_CTBENR, reg | (1 << pc->pc_cpuid)); } else { /* * Set BPTR to the physical address of the boot page */ bptr = (bptr >> 12) | 0x80000000u; ccsr_write4(OCP85XX_BPTR, bptr); __asm __volatile("isync; msync"); } /* * Release AP from hold-off state */ reg = ccsr_read4(brr); ccsr_write4(brr, reg | (1 << cpuid)); __asm __volatile("isync; msync"); spin_wait: timeout = 500; while (!pc->pc_awake && timeout--) DELAY(1000); /* wait 1ms */ /* * Disable boot page translation so that the 4K page at the default * address (= 0xfffff000) isn't permanently remapped and thus not * usable otherwise. */ if (!epapr_boot) { if (mpc85xx_is_qoriq()) ccsr_write4(OCP85XX_BSTAR, 0); else ccsr_write4(OCP85XX_BPTR, 0); __asm __volatile("isync; msync"); } if (!pc->pc_awake) panic("SMP: CPU %d didn't wake up.\n", pc->pc_cpuid); return ((pc->pc_awake) ? 0 : EBUSY); #else /* No SMP support */ return (ENXIO); #endif } static void mpc85xx_reset(platform_t plat) { /* * Try the dedicated reset register first. * If the SoC doesn't have one, we'll fall * back to using the debug control register. */ ccsr_write4(OCP85XX_RSTCR, 2); /* Clear DBCR0, disables debug interrupts and events. */ mtspr(SPR_DBCR0, 0); __asm __volatile("isync"); /* Enable Debug Interrupts in MSR. */ mtmsr(mfmsr() | PSL_DE); /* Enable debug interrupts and issue reset. */ mtspr(SPR_DBCR0, mfspr(SPR_DBCR0) | DBCR0_IDM | DBCR0_RST_SYSTEM); printf("Reset failed...\n"); while (1) ; } static void mpc85xx_smp_timebase_sync(platform_t plat, u_long tb, int ap) { mttb(tb); } Index: projects/clang700-import/sys/powerpc/powerpc/db_trace.c =================================================================== --- projects/clang700-import/sys/powerpc/powerpc/db_trace.c (revision 341812) +++ projects/clang700-import/sys/powerpc/powerpc/db_trace.c (revision 341813) @@ -1,319 +1,320 @@ /* $FreeBSD$ */ /* $NetBSD: db_trace.c,v 1.20 2002/05/13 20:30:09 matt Exp $ */ /* $OpenBSD: db_trace.c,v 1.3 1997/03/21 02:10:48 niklas Exp $ */ /*- * Mach Operating System * Copyright (c) 1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_frame; #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) #ifdef __powerpc64__ #define CALLOFFSET 8 /* Include TOC reload slot */ #else #define CALLOFFSET 4 #endif struct db_variable db_regs[] = { { "r0", DB_OFFSET(fixreg[0]), db_frame }, { "r1", DB_OFFSET(fixreg[1]), db_frame }, { "r2", DB_OFFSET(fixreg[2]), db_frame }, { "r3", DB_OFFSET(fixreg[3]), db_frame }, { "r4", DB_OFFSET(fixreg[4]), db_frame }, { "r5", DB_OFFSET(fixreg[5]), db_frame }, { "r6", DB_OFFSET(fixreg[6]), db_frame }, { "r7", DB_OFFSET(fixreg[7]), db_frame }, { "r8", DB_OFFSET(fixreg[8]), db_frame }, { "r9", DB_OFFSET(fixreg[9]), db_frame }, { "r10", DB_OFFSET(fixreg[10]), db_frame }, { "r11", DB_OFFSET(fixreg[11]), db_frame }, { "r12", DB_OFFSET(fixreg[12]), db_frame }, { "r13", DB_OFFSET(fixreg[13]), db_frame }, { "r14", DB_OFFSET(fixreg[14]), db_frame }, { "r15", DB_OFFSET(fixreg[15]), db_frame }, { "r16", DB_OFFSET(fixreg[16]), db_frame }, { "r17", DB_OFFSET(fixreg[17]), db_frame }, { "r18", DB_OFFSET(fixreg[18]), db_frame }, { "r19", DB_OFFSET(fixreg[19]), db_frame }, { "r20", DB_OFFSET(fixreg[20]), db_frame }, { "r21", DB_OFFSET(fixreg[21]), db_frame }, { "r22", DB_OFFSET(fixreg[22]), db_frame }, { "r23", DB_OFFSET(fixreg[23]), db_frame }, { "r24", DB_OFFSET(fixreg[24]), db_frame }, { "r25", DB_OFFSET(fixreg[25]), db_frame }, { "r26", DB_OFFSET(fixreg[26]), db_frame }, { "r27", DB_OFFSET(fixreg[27]), db_frame }, { "r28", DB_OFFSET(fixreg[28]), db_frame }, { "r29", DB_OFFSET(fixreg[29]), db_frame }, { "r30", DB_OFFSET(fixreg[30]), db_frame }, { "r31", DB_OFFSET(fixreg[31]), db_frame }, { "srr0", DB_OFFSET(srr0), db_frame }, { "srr1", DB_OFFSET(srr1), db_frame }, { "lr", DB_OFFSET(lr), db_frame }, { "ctr", DB_OFFSET(ctr), db_frame }, { "cr", DB_OFFSET(cr), db_frame }, { "xer", DB_OFFSET(xer), db_frame }, { "dar", DB_OFFSET(dar), db_frame }, #ifdef AIM { "dsisr", DB_OFFSET(cpu.aim.dsisr), db_frame }, #endif #if defined(BOOKE) { "esr", DB_OFFSET(cpu.booke.esr), db_frame }, #endif }; struct db_variable *db_eregs = db_regs + nitems(db_regs); /* * register variable handling */ static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { register_t *reg; if (kdb_frame == NULL) return (0); reg = (register_t*)((uintptr_t)kdb_frame + (uintptr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } /* * Frame tracing. */ static int db_backtrace(struct thread *td, db_addr_t fp, int count) { db_addr_t stackframe, lr, *args; boolean_t kernel_only = TRUE; boolean_t full = FALSE; #if 0 { register char *cp = modif; register char c; while ((c = *cp++) != 0) { if (c == 't') trace_thread = TRUE; if (c == 'u') kernel_only = FALSE; if (c == 'f') full = TRUE; } } #endif stackframe = fp; while (!db_pager_quit) { if (stackframe < PAGE_SIZE) break; /* * Locate the next frame by grabbing the backchain ptr * from frame[0] */ stackframe = *(db_addr_t *)stackframe; next_frame: #ifdef __powerpc64__ /* The saved arg values start at frame[6] */ args = (db_addr_t *)(stackframe + 48); #else /* The saved arg values start at frame[2] */ args = (db_addr_t *)(stackframe + 8); #endif if (stackframe < PAGE_SIZE) break; if (count-- == 0) break; /* * Extract link register from frame and subtract * 4 to convert into calling address (as opposed to * return address) */ #ifdef __powerpc64__ lr = *(db_addr_t *)(stackframe + 16) - 4; #else lr = *(db_addr_t *)(stackframe + 4) - 4; #endif if ((lr & 3) || (lr < 0x100)) { db_printf("saved LR(0x%zx) is invalid.", lr); break; } #ifdef __powerpc64__ db_printf("0x%016lx: ", stackframe); #else db_printf("0x%08x: ", stackframe); #endif /* * The trap code labels the return addresses from the * call to C code as 'trapexit' and 'asttrapexit. Use this * to determine if the callframe has to traverse a saved * trap context */ if ((lr + CALLOFFSET == (db_addr_t) &trapexit) || (lr + CALLOFFSET == (db_addr_t) &asttrapexit)) { const char *trapstr; struct trapframe *tf = (struct trapframe *)(args); db_printf("%s ", tf->srr1 & PSL_PR ? "user" : "kernel"); switch (tf->exc) { case EXC_DSI: /* XXX take advantage of the union. */ #ifdef BOOKE db_printf("DSI %s trap @ %#zx by ", (tf->cpu.booke.esr & ESR_ST) ? "write" : "read", tf->dar); #else db_printf("DSI %s trap @ %#zx by ", (tf->cpu.aim.dsisr & DSISR_STORE) ? "write" : "read", tf->dar); #endif goto print_trap; case EXC_ALI: db_printf("ALI trap @ %#zx (xSR %#x) ", tf->dar, (uint32_t)tf->cpu.aim.dsisr); goto print_trap; #ifdef __powerpc64__ case EXC_DSE: db_printf("DSE trap @ %#zx by ", tf->dar); goto print_trap; case EXC_ISE: db_printf("ISE trap @ %#zx by ", tf->srr0); goto print_trap; #endif case EXC_ISI: trapstr = "ISI"; break; case EXC_PGM: trapstr = "PGM"; break; case EXC_SC: trapstr = "SC"; break; case EXC_EXI: trapstr = "EXI"; break; case EXC_MCHK: trapstr = "MCHK"; break; case EXC_VEC: trapstr = "VEC"; break; #if !defined(BOOKE) case EXC_FPA: trapstr = "FPA"; break; case EXC_BPT: trapstr = "BPT"; break; case EXC_TRC: trapstr = "TRC"; break; case EXC_RUNMODETRC: trapstr = "RUNMODETRC"; break; case EXC_SMI: trapstr = "SMI"; break; case EXC_RST: trapstr = "RST"; break; #endif case EXC_FPU: trapstr = "FPU"; break; case EXC_DECR: trapstr = "DECR"; break; case EXC_PERF: trapstr = "PERF"; break; case EXC_VSX: trapstr = "VSX"; break; + case EXC_SOFT_PATCH: trapstr = "SOFT_PATCH"; break; default: trapstr = NULL; break; } if (trapstr != NULL) { db_printf("%s trap by ", trapstr); } else { db_printf("trap %#zx by ", tf->exc); } print_trap: lr = (db_addr_t) tf->srr0; db_printsym(lr, DB_STGY_ANY); db_printf(": srr1=%#zx\n", tf->srr1); db_printf("%-10s r1=%#zx cr=%#x xer=%#x ctr=%#zx", "", tf->fixreg[1], (uint32_t)tf->cr, (uint32_t)tf->xer, tf->ctr); #ifdef __powerpc64__ db_printf(" r2=%#zx", tf->fixreg[2]); #endif if (tf->exc == EXC_DSI) db_printf(" sr=%#x", (uint32_t)tf->cpu.aim.dsisr); db_printf("\n"); stackframe = (db_addr_t) tf->fixreg[1]; if (kernel_only && (tf->srr1 & PSL_PR)) break; goto next_frame; } db_printf("at "); db_printsym(lr, DB_STGY_PROC); if (full) /* Print all the args stored in that stackframe. */ db_printf("(%zx, %zx, %zx, %zx, %zx, %zx, %zx, %zx)", args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); db_printf("\n"); } return (0); } void db_trace_self(void) { db_addr_t addr; addr = (db_addr_t)__builtin_frame_address(0); if (addr == 0) { db_printf("Null frame address\n"); return; } db_backtrace(curthread, *(db_addr_t *)addr, -1); } int db_trace_thread(struct thread *td, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(td); return (db_backtrace(td, (db_addr_t)ctx->pcb_sp, count)); } Index: projects/clang700-import/sys/powerpc/powerpc/trap.c =================================================================== --- projects/clang700-import/sys/powerpc/powerpc/trap.c (revision 341812) +++ projects/clang700-import/sys/powerpc/powerpc/trap.c (revision 341813) @@ -1,936 +1,992 @@ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: trap.c,v 1.58 2002/03/04 04:07:35 dbj Exp $ */ #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 #include #include #include #include #include #include /* Below matches setjmp.S */ #define FAULTBUF_LR 21 #define FAULTBUF_R1 1 #define FAULTBUF_R2 2 #define FAULTBUF_CR 22 #define FAULTBUF_R14 3 #define MOREARGS(sp) ((caddr_t)((uintptr_t)(sp) + \ sizeof(struct callframe) - 3*sizeof(register_t))) /* more args go here */ static void trap_fatal(struct trapframe *frame); static void printtrap(u_int vector, struct trapframe *frame, int isfatal, int user); static int trap_pfault(struct trapframe *frame, int user); static int fix_unaligned(struct thread *td, struct trapframe *frame); static int handle_onfault(struct trapframe *frame); static void syscall(struct trapframe *frame); #if defined(__powerpc64__) && defined(AIM) void handle_kernel_slb_spill(int, register_t, register_t); static int handle_user_slb_spill(pmap_t pm, vm_offset_t addr); extern int n_slbs; +static void normalize_inputs(void); #endif extern vm_offset_t __startkernel; #ifdef KDB int db_trap_glue(struct trapframe *); /* Called from trap_subr.S */ #endif struct powerpc_exception { u_int vector; char *name; }; #ifdef KDTRACE_HOOKS #include int (*dtrace_invop_jump_addr)(struct trapframe *); #endif static struct powerpc_exception powerpc_exceptions[] = { { EXC_CRIT, "critical input" }, { EXC_RST, "system reset" }, { EXC_MCHK, "machine check" }, { EXC_DSI, "data storage interrupt" }, { EXC_DSE, "data segment exception" }, { EXC_ISI, "instruction storage interrupt" }, { EXC_ISE, "instruction segment exception" }, { EXC_EXI, "external interrupt" }, { EXC_ALI, "alignment" }, { EXC_PGM, "program" }, { EXC_HEA, "hypervisor emulation assistance" }, { EXC_FPU, "floating-point unavailable" }, { EXC_APU, "auxiliary proc unavailable" }, { EXC_DECR, "decrementer" }, { EXC_FIT, "fixed-interval timer" }, { EXC_WDOG, "watchdog timer" }, { EXC_SC, "system call" }, { EXC_TRC, "trace" }, { EXC_FPA, "floating-point assist" }, { EXC_DEBUG, "debug" }, { EXC_PERF, "performance monitoring" }, { EXC_VEC, "altivec unavailable" }, { EXC_VSX, "vsx unavailable" }, { EXC_FAC, "facility unavailable" }, { EXC_ITMISS, "instruction tlb miss" }, { EXC_DLMISS, "data load tlb miss" }, { EXC_DSMISS, "data store tlb miss" }, { EXC_BPT, "instruction breakpoint" }, { EXC_SMI, "system management" }, { EXC_VECAST_G4, "altivec assist" }, { EXC_THRM, "thermal management" }, { EXC_RUNMODETRC, "run mode/trace" }, + { EXC_SOFT_PATCH, "soft patch exception" }, { EXC_LAST, NULL } }; #define ESR_BITMASK \ "\20" \ "\040b0\037b1\036b2\035b3\034PIL\033PRR\032PTR\031FP" \ "\030ST\027b9\026DLK\025ILK\024b12\023b13\022BO\021PIE" \ "\020b16\017b17\016b18\015b19\014b20\013b21\012b22\011b23" \ "\010SPE\007EPID\006b26\005b27\004b28\003b29\002b30\001b31" #define MCSR_BITMASK \ "\20" \ "\040MCP\037ICERR\036DCERR\035TLBPERR\034L2MMU_MHIT\033b5\032b6\031b7" \ "\030b8\027b9\026b10\025NMI\024MAV\023MEA\022b14\021IF" \ "\020LD\017ST\016LDG\015b19\014b20\013b21\012b22\011b23" \ "\010b24\007b25\006b26\005b27\004b28\003b29\002TLBSYNC\001BSL2_ERR" #define MSSSR_BITMASK \ "\20" \ "\040b0\037b1\036b2\035b3\034b4\033b5\032b6\031b7" \ "\030b8\027b9\026b10\025b11\024b12\023L2TAG\022L2DAT\021L3TAG" \ "\020L3DAT\017APE\016DPE\015TEA\014b20\013b21\012b22\011b23" \ "\010b24\007b25\006b26\005b27\004b28\003b29\002b30\001b31" static const char * trapname(u_int vector) { struct powerpc_exception *pe; for (pe = powerpc_exceptions; pe->vector != EXC_LAST; pe++) { if (pe->vector == vector) return (pe->name); } return ("unknown"); } static inline bool frame_is_trap_inst(struct trapframe *frame) { #ifdef AIM return (frame->exc == EXC_PGM && frame->srr1 & EXC_PGM_TRAP); #else return ((frame->cpu.booke.esr & ESR_PTR) != 0); #endif } void trap(struct trapframe *frame) { struct thread *td; struct proc *p; #ifdef KDTRACE_HOOKS uint32_t inst; #endif int sig, type, user; u_int ucode; ksiginfo_t ksi; register_t fscr; VM_CNT_INC(v_trap); #ifdef KDB if (kdb_active) { kdb_reenter(); return; } #endif td = curthread; p = td->td_proc; type = ucode = frame->exc; sig = 0; user = frame->srr1 & PSL_PR; CTR3(KTR_TRAP, "trap: %s type=%s (%s)", td->td_name, trapname(type), user ? "user" : "kernel"); #ifdef KDTRACE_HOOKS /* * A trap can occur while DTrace executes a probe. Before * executing the probe, DTrace blocks re-scheduling and sets * a flag in its per-cpu flags to indicate that it doesn't * want to fault. On returning from the probe, the no-fault * flag is cleared and finally re-scheduling is enabled. * * If the DTrace kernel module has registered a trap handler, * call it and if it returns non-zero, assume that it has * handled the trap and modified the trap frame so that this * function can return normally. */ if (dtrace_trap_func != NULL && (*dtrace_trap_func)(frame, type) != 0) return; #endif if (user) { td->td_pticks = 0; td->td_frame = frame; if (td->td_cowgen != p->p_cowgen) thread_cow_update(td); /* User Mode Traps */ switch (type) { case EXC_RUNMODETRC: case EXC_TRC: frame->srr1 &= ~PSL_SE; sig = SIGTRAP; ucode = TRAP_TRACE; break; #if defined(__powerpc64__) && defined(AIM) case EXC_ISE: case EXC_DSE: if (handle_user_slb_spill(&p->p_vmspace->vm_pmap, (type == EXC_ISE) ? frame->srr0 : frame->dar) != 0){ sig = SIGSEGV; ucode = SEGV_MAPERR; } break; #endif case EXC_DSI: case EXC_ISI: sig = trap_pfault(frame, 1); if (sig == SIGSEGV) ucode = SEGV_MAPERR; break; case EXC_SC: syscall(frame); break; case EXC_FPU: KASSERT((td->td_pcb->pcb_flags & PCB_FPU) != PCB_FPU, ("FPU already enabled for thread")); enable_fpu(td); break; case EXC_VEC: KASSERT((td->td_pcb->pcb_flags & PCB_VEC) != PCB_VEC, ("Altivec already enabled for thread")); enable_vec(td); break; case EXC_VSX: KASSERT((td->td_pcb->pcb_flags & PCB_VSX) != PCB_VSX, ("VSX already enabled for thread")); if (!(td->td_pcb->pcb_flags & PCB_VEC)) enable_vec(td); if (!(td->td_pcb->pcb_flags & PCB_FPU)) save_fpu(td); td->td_pcb->pcb_flags |= PCB_VSX; enable_fpu(td); break; case EXC_FAC: fscr = mfspr(SPR_FSCR); if ((fscr & FSCR_IC_MASK) == FSCR_IC_HTM) { CTR0(KTR_TRAP, "Hardware Transactional Memory subsystem disabled"); } sig = SIGILL; ucode = ILL_ILLOPC; break; case EXC_HEA: sig = SIGILL; ucode = ILL_ILLOPC; break; case EXC_VECAST_E: case EXC_VECAST_G4: case EXC_VECAST_G5: /* * We get a VPU assist exception for IEEE mode * vector operations on denormalized floats. * Emulating this is a giant pain, so for now, * just switch off IEEE mode and treat them as * zero. */ save_vec(td); td->td_pcb->pcb_vec.vscr |= ALTIVEC_VSCR_NJ; enable_vec(td); break; case EXC_ALI: if (fix_unaligned(td, frame) != 0) { sig = SIGBUS; ucode = BUS_ADRALN; } else frame->srr0 += 4; break; case EXC_DEBUG: /* Single stepping */ mtspr(SPR_DBSR, mfspr(SPR_DBSR)); frame->srr1 &= ~PSL_DE; frame->cpu.booke.dbcr0 &= ~(DBCR0_IDM | DBCR0_IC); sig = SIGTRAP; ucode = TRAP_TRACE; break; case EXC_PGM: /* Identify the trap reason */ if (frame_is_trap_inst(frame)) { #ifdef KDTRACE_HOOKS inst = fuword32((const void *)frame->srr0); if (inst == 0x0FFFDDDD && dtrace_pid_probe_ptr != NULL) { (*dtrace_pid_probe_ptr)(frame); break; } #endif sig = SIGTRAP; ucode = TRAP_BRKPT; } else { sig = ppc_instr_emulate(frame, td->td_pcb); if (sig == SIGILL) { if (frame->srr1 & EXC_PGM_PRIV) ucode = ILL_PRVOPC; else if (frame->srr1 & EXC_PGM_ILLEGAL) ucode = ILL_ILLOPC; } else if (sig == SIGFPE) ucode = FPE_FLTINV; /* Punt for now, invalid operation. */ } break; case EXC_MCHK: /* * Note that this may not be recoverable for the user * process, depending on the type of machine check, * but it at least prevents the kernel from dying. */ sig = SIGBUS; ucode = BUS_OBJERR; break; +#if defined(__powerpc64__) && defined(AIM) + case EXC_SOFT_PATCH: + /* + * Point to the instruction that generated the exception to execute it again, + * and normalize the register values. + */ + frame->srr0 -= 4; + normalize_inputs(); + break; +#endif + default: trap_fatal(frame); } } else { /* Kernel Mode Traps */ KASSERT(cold || td->td_ucred != NULL, ("kernel trap doesn't have ucred")); switch (type) { case EXC_PGM: #ifdef KDTRACE_HOOKS if (frame_is_trap_inst(frame)) { if (*(uint32_t *)frame->srr0 == EXC_DTRACE) { if (dtrace_invop_jump_addr != NULL) { dtrace_invop_jump_addr(frame); return; } } } #endif #ifdef KDB if (db_trap_glue(frame)) return; #endif break; #if defined(__powerpc64__) && defined(AIM) case EXC_DSE: if (td->td_pcb->pcb_cpu.aim.usr_vsid != 0 && (frame->dar & SEGMENT_MASK) == USER_ADDR) { __asm __volatile ("slbmte %0, %1" :: "r"(td->td_pcb->pcb_cpu.aim.usr_vsid), "r"(USER_SLB_SLBE)); return; } break; #endif case EXC_DSI: if (trap_pfault(frame, 0) == 0) return; break; case EXC_MCHK: if (handle_onfault(frame)) return; break; default: break; } trap_fatal(frame); } if (sig != 0) { if (p->p_sysent->sv_transtrap != NULL) sig = (p->p_sysent->sv_transtrap)(sig, type); ksiginfo_init_trap(&ksi); ksi.ksi_signo = sig; ksi.ksi_code = (int) ucode; /* XXX, not POSIX */ ksi.ksi_addr = (void *)frame->srr0; ksi.ksi_trapno = type; trapsignal(td, &ksi); } userret(td, frame); } static void trap_fatal(struct trapframe *frame) { #ifdef KDB bool handled; #endif printtrap(frame->exc, frame, 1, (frame->srr1 & PSL_PR)); #ifdef KDB if (debugger_on_trap) { kdb_why = KDB_WHY_TRAP; handled = kdb_trap(frame->exc, 0, frame); kdb_why = KDB_WHY_UNSET; if (handled) return; } #endif panic("%s trap", trapname(frame->exc)); } static void cpu_printtrap(u_int vector, struct trapframe *frame, int isfatal, int user) { #ifdef AIM uint16_t ver; switch (vector) { case EXC_DSE: case EXC_DSI: case EXC_DTMISS: printf(" dsisr = 0x%lx\n", (u_long)frame->cpu.aim.dsisr); break; case EXC_MCHK: ver = mfpvr() >> 16; if (MPC745X_P(ver)) printf(" msssr0 = 0x%b\n", (int)mfspr(SPR_MSSSR0), MSSSR_BITMASK); break; } #elif defined(BOOKE) vm_paddr_t pa; switch (vector) { case EXC_MCHK: pa = mfspr(SPR_MCARU); pa = (pa << 32) | (u_register_t)mfspr(SPR_MCAR); printf(" mcsr = 0x%b\n", (int)mfspr(SPR_MCSR), MCSR_BITMASK); printf(" mcar = 0x%jx\n", (uintmax_t)pa); } printf(" esr = 0x%b\n", (int)frame->cpu.booke.esr, ESR_BITMASK); #endif } static void printtrap(u_int vector, struct trapframe *frame, int isfatal, int user) { printf("\n"); printf("%s %s trap:\n", isfatal ? "fatal" : "handled", user ? "user" : "kernel"); printf("\n"); printf(" exception = 0x%x (%s)\n", vector, trapname(vector)); switch (vector) { case EXC_DSE: case EXC_DSI: case EXC_DTMISS: printf(" virtual address = 0x%" PRIxPTR "\n", frame->dar); break; case EXC_ISE: case EXC_ISI: case EXC_ITMISS: printf(" virtual address = 0x%" PRIxPTR "\n", frame->srr0); break; case EXC_MCHK: break; } cpu_printtrap(vector, frame, isfatal, user); printf(" srr0 = 0x%" PRIxPTR " (0x%" PRIxPTR ")\n", frame->srr0, frame->srr0 - (register_t)(__startkernel - KERNBASE)); printf(" srr1 = 0x%lx\n", (u_long)frame->srr1); printf(" current msr = 0x%" PRIxPTR "\n", mfmsr()); printf(" lr = 0x%" PRIxPTR " (0x%" PRIxPTR ")\n", frame->lr, frame->lr - (register_t)(__startkernel - KERNBASE)); printf(" curthread = %p\n", curthread); if (curthread != NULL) printf(" pid = %d, comm = %s\n", curthread->td_proc->p_pid, curthread->td_name); printf("\n"); } /* * Handles a fatal fault when we have onfault state to recover. Returns * non-zero if there was onfault recovery state available. */ static int handle_onfault(struct trapframe *frame) { struct thread *td; jmp_buf *fb; td = curthread; fb = td->td_pcb->pcb_onfault; if (fb != NULL) { frame->srr0 = (*fb)->_jb[FAULTBUF_LR]; frame->fixreg[1] = (*fb)->_jb[FAULTBUF_R1]; frame->fixreg[2] = (*fb)->_jb[FAULTBUF_R2]; frame->fixreg[3] = 1; frame->cr = (*fb)->_jb[FAULTBUF_CR]; bcopy(&(*fb)->_jb[FAULTBUF_R14], &frame->fixreg[14], 18 * sizeof(register_t)); td->td_pcb->pcb_onfault = NULL; /* Returns twice, not thrice */ return (1); } return (0); } int cpu_fetch_syscall_args(struct thread *td) { struct proc *p; struct trapframe *frame; struct syscall_args *sa; caddr_t params; size_t argsz; int error, n, i; p = td->td_proc; frame = td->td_frame; sa = &td->td_sa; sa->code = frame->fixreg[0]; params = (caddr_t)(frame->fixreg + FIRSTARG); n = NARGREG; if (sa->code == SYS_syscall) { /* * code is first argument, * followed by actual args. */ sa->code = *(register_t *) params; params += sizeof(register_t); n -= 1; } else if (sa->code == SYS___syscall) { /* * Like syscall, but code is a quad, * so as to maintain quad alignment * for the rest of the args. */ if (SV_PROC_FLAG(p, SV_ILP32)) { params += sizeof(register_t); sa->code = *(register_t *) params; params += sizeof(register_t); n -= 2; } else { sa->code = *(register_t *) params; params += sizeof(register_t); n -= 1; } } if (p->p_sysent->sv_mask) sa->code &= p->p_sysent->sv_mask; if (sa->code >= p->p_sysent->sv_size) sa->callp = &p->p_sysent->sv_table[0]; else sa->callp = &p->p_sysent->sv_table[sa->code]; sa->narg = sa->callp->sy_narg; if (SV_PROC_FLAG(p, SV_ILP32)) { argsz = sizeof(uint32_t); for (i = 0; i < n; i++) sa->args[i] = ((u_register_t *)(params))[i] & 0xffffffff; } else { argsz = sizeof(uint64_t); for (i = 0; i < n; i++) sa->args[i] = ((u_register_t *)(params))[i]; } if (sa->narg > n) error = copyin(MOREARGS(frame->fixreg[1]), sa->args + n, (sa->narg - n) * argsz); else error = 0; #ifdef __powerpc64__ if (SV_PROC_FLAG(p, SV_ILP32) && sa->narg > n) { /* Expand the size of arguments copied from the stack */ for (i = sa->narg; i >= n; i--) sa->args[i] = ((uint32_t *)(&sa->args[n]))[i-n]; } #endif if (error == 0) { td->td_retval[0] = 0; td->td_retval[1] = frame->fixreg[FIRSTARG + 1]; } return (error); } #include "../../kern/subr_syscall.c" void syscall(struct trapframe *frame) { struct thread *td; int error; td = curthread; td->td_frame = frame; #if defined(__powerpc64__) && defined(AIM) /* * Speculatively restore last user SLB segment, which we know is * invalid already, since we are likely to do copyin()/copyout(). */ if (td->td_pcb->pcb_cpu.aim.usr_vsid != 0) __asm __volatile ("slbmte %0, %1; isync" :: "r"(td->td_pcb->pcb_cpu.aim.usr_vsid), "r"(USER_SLB_SLBE)); #endif error = syscallenter(td); syscallret(td, error); } #if defined(__powerpc64__) && defined(AIM) /* Handle kernel SLB faults -- runs in real mode, all seat belts off */ void handle_kernel_slb_spill(int type, register_t dar, register_t srr0) { struct slb *slbcache; uint64_t slbe, slbv; uint64_t esid, addr; int i; addr = (type == EXC_ISE) ? srr0 : dar; slbcache = PCPU_GET(aim.slb); esid = (uintptr_t)addr >> ADDR_SR_SHFT; slbe = (esid << SLBE_ESID_SHIFT) | SLBE_VALID; /* See if the hardware flushed this somehow (can happen in LPARs) */ for (i = 0; i < n_slbs; i++) if (slbcache[i].slbe == (slbe | (uint64_t)i)) return; /* Not in the map, needs to actually be added */ slbv = kernel_va_to_slbv(addr); if (slbcache[USER_SLB_SLOT].slbe == 0) { for (i = 0; i < n_slbs; i++) { if (i == USER_SLB_SLOT) continue; if (!(slbcache[i].slbe & SLBE_VALID)) goto fillkernslb; } if (i == n_slbs) slbcache[USER_SLB_SLOT].slbe = 1; } /* Sacrifice a random SLB entry that is not the user entry */ i = mftb() % n_slbs; if (i == USER_SLB_SLOT) i = (i+1) % n_slbs; fillkernslb: /* Write new entry */ slbcache[i].slbv = slbv; slbcache[i].slbe = slbe | (uint64_t)i; /* Trap handler will restore from cache on exit */ } static int handle_user_slb_spill(pmap_t pm, vm_offset_t addr) { struct slb *user_entry; uint64_t esid; int i; if (pm->pm_slb == NULL) return (-1); esid = (uintptr_t)addr >> ADDR_SR_SHFT; PMAP_LOCK(pm); user_entry = user_va_to_slb_entry(pm, addr); if (user_entry == NULL) { /* allocate_vsid auto-spills it */ (void)allocate_user_vsid(pm, esid, 0); } else { /* * Check that another CPU has not already mapped this. * XXX: Per-thread SLB caches would be better. */ for (i = 0; i < pm->pm_slb_len; i++) if (pm->pm_slb[i] == user_entry) break; if (i == pm->pm_slb_len) slb_insert_user(pm, user_entry); } PMAP_UNLOCK(pm); return (0); } #endif static int trap_pfault(struct trapframe *frame, int user) { vm_offset_t eva, va; struct thread *td; struct proc *p; vm_map_t map; vm_prot_t ftype; int rv, is_user; td = curthread; p = td->td_proc; if (frame->exc == EXC_ISI) { eva = frame->srr0; ftype = VM_PROT_EXECUTE; if (frame->srr1 & SRR1_ISI_PFAULT) ftype |= VM_PROT_READ; } else { eva = frame->dar; #ifdef BOOKE if (frame->cpu.booke.esr & ESR_ST) #else if (frame->cpu.aim.dsisr & DSISR_STORE) #endif ftype = VM_PROT_WRITE; else ftype = VM_PROT_READ; } if (user) { KASSERT(p->p_vmspace != NULL, ("trap_pfault: vmspace NULL")); map = &p->p_vmspace->vm_map; } else { rv = pmap_decode_kernel_ptr(eva, &is_user, &eva); if (rv != 0) return (SIGSEGV); if (is_user) map = &p->p_vmspace->vm_map; else map = kernel_map; } va = trunc_page(eva); /* Fault in the page. */ rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL); /* * XXXDTRACE: add dtrace_doubletrap_func here? */ if (rv == KERN_SUCCESS) return (0); if (!user && handle_onfault(frame)) return (0); return (SIGSEGV); } /* * For now, this only deals with the particular unaligned access case * that gcc tends to generate. Eventually it should handle all of the * possibilities that can happen on a 32-bit PowerPC in big-endian mode. */ static int fix_unaligned(struct thread *td, struct trapframe *frame) { struct thread *fputhread; #ifdef __SPE__ uint32_t inst; #endif int indicator, reg; double *fpr; #ifdef __SPE__ indicator = (frame->cpu.booke.esr & (ESR_ST|ESR_SPE)); if (indicator & ESR_SPE) { if (copyin((void *)frame->srr0, &inst, sizeof(inst)) != 0) return (-1); reg = EXC_ALI_SPE_REG(inst); fpr = (double *)td->td_pcb->pcb_vec.vr[reg]; fputhread = PCPU_GET(vecthread); /* Juggle the SPE to ensure that we've initialized * the registers, and that their current state is in * the PCB. */ if (fputhread != td) { if (fputhread) save_vec(fputhread); enable_vec(td); } save_vec(td); if (!(indicator & ESR_ST)) { if (copyin((void *)frame->dar, fpr, sizeof(double)) != 0) return (-1); frame->fixreg[reg] = td->td_pcb->pcb_vec.vr[reg][1]; enable_vec(td); } else { td->td_pcb->pcb_vec.vr[reg][1] = frame->fixreg[reg]; if (copyout(fpr, (void *)frame->dar, sizeof(double)) != 0) return (-1); } return (0); } #else indicator = EXC_ALI_OPCODE_INDICATOR(frame->cpu.aim.dsisr); switch (indicator) { case EXC_ALI_LFD: case EXC_ALI_STFD: reg = EXC_ALI_RST(frame->cpu.aim.dsisr); fpr = &td->td_pcb->pcb_fpu.fpr[reg].fpr; fputhread = PCPU_GET(fputhread); /* Juggle the FPU to ensure that we've initialized * the FPRs, and that their current state is in * the PCB. */ if (fputhread != td) { if (fputhread) save_fpu(fputhread); enable_fpu(td); } save_fpu(td); if (indicator == EXC_ALI_LFD) { if (copyin((void *)frame->dar, fpr, sizeof(double)) != 0) return (-1); enable_fpu(td); } else { if (copyout(fpr, (void *)frame->dar, sizeof(double)) != 0) return (-1); } return (0); break; } #endif return (-1); } + +#if defined(__powerpc64__) && defined(AIM) +#define MSKNSHL(x, m, n) "(((" #x ") & " #m ") << " #n ")" +#define MSKNSHR(x, m, n) "(((" #x ") & " #m ") >> " #n ")" + +/* xvcpsgndp instruction, built in opcode format. + * This can be changed to use mnemonic after a toolchain update. + */ +#define XVCPSGNDP(xt, xa, xb) \ + __asm __volatile(".long (" \ + MSKNSHL(60, 0x3f, 26) " | " \ + MSKNSHL(xt, 0x1f, 21) " | " \ + MSKNSHL(xa, 0x1f, 16) " | " \ + MSKNSHL(xb, 0x1f, 11) " | " \ + MSKNSHL(240, 0xff, 3) " | " \ + MSKNSHR(xa, 0x20, 3) " | " \ + MSKNSHR(xa, 0x20, 4) " | " \ + MSKNSHR(xa, 0x20, 5) ")") + +/* Macros to normalize 1 or 10 VSX registers */ +#define NORM(x) XVCPSGNDP(x, x, x) +#define NORM10(x) \ + NORM(x ## 0); NORM(x ## 1); NORM(x ## 2); NORM(x ## 3); NORM(x ## 4); \ + NORM(x ## 5); NORM(x ## 6); NORM(x ## 7); NORM(x ## 8); NORM(x ## 9) + +static void +normalize_inputs(void) +{ + unsigned long msr; + + /* enable VSX */ + msr = mfmsr(); + mtmsr(msr | PSL_VSX); + + NORM(0); NORM(1); NORM(2); NORM(3); NORM(4); + NORM(5); NORM(6); NORM(7); NORM(8); NORM(9); + NORM10(1); NORM10(2); NORM10(3); NORM10(4); NORM10(5); + NORM(60); NORM(61); NORM(62); NORM(63); + + /* restore MSR */ + mtmsr(msr); +} +#endif #ifdef KDB int db_trap_glue(struct trapframe *frame) { if (!(frame->srr1 & PSL_PR) && (frame->exc == EXC_TRC || frame->exc == EXC_RUNMODETRC || frame_is_trap_inst(frame) || frame->exc == EXC_BPT || frame->exc == EXC_DEBUG || frame->exc == EXC_DSI)) { int type = frame->exc; /* Ignore DTrace traps. */ if (*(uint32_t *)frame->srr0 == EXC_DTRACE) return (0); if (frame_is_trap_inst(frame)) { type = T_BREAKPOINT; } return (kdb_trap(type, 0, frame)); } return (0); } #endif Index: projects/clang700-import/sys/riscv/riscv/pmap.c =================================================================== --- projects/clang700-import/sys/riscv/riscv/pmap.c (revision 341812) +++ projects/clang700-import/sys/riscv/riscv/pmap.c (revision 341813) @@ -1,3423 +1,3390 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 John S. Dyson * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * Copyright (c) 2003 Peter Wemm * All rights reserved. * Copyright (c) 2005-2010 Alan L. Cox * All rights reserved. * Copyright (c) 2014 Andrew Turner * All rights reserved. * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * Copyright (c) 2015-2018 Ruslan Bukin * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department and William Jolitz of UUNET Technologies Inc. * * Portions of this software were developed by Andrew Turner under * sponsorship from The FreeBSD Foundation. * * Portions of this software were developed by SRI International and the * University of Cambridge Computer Laboratory under DARPA/AFRL contract * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. * * Portions of this software were developed by the University of Cambridge * Computer Laboratory as part of the CTSRD Project, with support from the * UK Higher Education Innovation Fund (HEIF). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91 */ /*- * Copyright (c) 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Jake Burkholder, * Safeport Network Services, and Network Associates Laboratories, the * Security Research Division of Network Associates, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 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$"); /* * Manages physical address maps. * * Since the information managed by this module is * also stored by the logical address mapping module, * this module may throw away valid virtual-to-physical * mappings at almost any time. However, invalidations * of virtual-to-physical mappings must be done as * requested. * * In order to cope with hardware architectures which * make virtual-to-physical map invalidates expensive, * this module may delay invalidate or reduced protection * operations until such time as they are actually * necessary. This module is given full information as * to which processors are currently using which maps, * and to when physical maps must be made correct. */ #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 #define NPDEPG (PAGE_SIZE/(sizeof (pd_entry_t))) #define NUPDE (NPDEPG * NPDEPG) #define NUSERPGTBLS (NUPDE + NPDEPG) #if !defined(DIAGNOSTIC) #ifdef __GNUC_GNU_INLINE__ #define PMAP_INLINE __attribute__((__gnu_inline__)) inline #else #define PMAP_INLINE extern inline #endif #else #define PMAP_INLINE #endif #ifdef PV_STATS #define PV_STAT(x) do { x ; } while (0) #else #define PV_STAT(x) do { } while (0) #endif #define pmap_l2_pindex(v) ((v) >> L2_SHIFT) #define NPV_LIST_LOCKS MAXCPU #define PHYS_TO_PV_LIST_LOCK(pa) \ (&pv_list_locks[pa_index(pa) % NPV_LIST_LOCKS]) #define CHANGE_PV_LIST_LOCK_TO_PHYS(lockp, pa) do { \ struct rwlock **_lockp = (lockp); \ struct rwlock *_new_lock; \ \ _new_lock = PHYS_TO_PV_LIST_LOCK(pa); \ if (_new_lock != *_lockp) { \ if (*_lockp != NULL) \ rw_wunlock(*_lockp); \ *_lockp = _new_lock; \ rw_wlock(*_lockp); \ } \ } while (0) #define CHANGE_PV_LIST_LOCK_TO_VM_PAGE(lockp, m) \ CHANGE_PV_LIST_LOCK_TO_PHYS(lockp, VM_PAGE_TO_PHYS(m)) #define RELEASE_PV_LIST_LOCK(lockp) do { \ struct rwlock **_lockp = (lockp); \ \ if (*_lockp != NULL) { \ rw_wunlock(*_lockp); \ *_lockp = NULL; \ } \ } while (0) #define VM_PAGE_TO_PV_LIST_LOCK(m) \ PHYS_TO_PV_LIST_LOCK(VM_PAGE_TO_PHYS(m)) /* The list of all the user pmaps */ LIST_HEAD(pmaplist, pmap); static struct pmaplist allpmaps; -static MALLOC_DEFINE(M_VMPMAP, "pmap", "PMAP L1"); - struct pmap kernel_pmap_store; vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss) */ vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */ vm_offset_t kernel_vm_end = 0; vm_paddr_t dmap_phys_base; /* The start of the dmap region */ vm_paddr_t dmap_phys_max; /* The limit of the dmap region */ vm_offset_t dmap_max_addr; /* The virtual address limit of the dmap */ /* This code assumes all L1 DMAP entries will be used */ CTASSERT((DMAP_MIN_ADDRESS & ~L1_OFFSET) == DMAP_MIN_ADDRESS); CTASSERT((DMAP_MAX_ADDRESS & ~L1_OFFSET) == DMAP_MAX_ADDRESS); static struct rwlock_padalign pvh_global_lock; /* * Data for the pv entry allocation mechanism */ static TAILQ_HEAD(pch, pv_chunk) pv_chunks = TAILQ_HEAD_INITIALIZER(pv_chunks); static struct mtx pv_chunks_mutex; static struct rwlock pv_list_locks[NPV_LIST_LOCKS]; static void free_pv_chunk(struct pv_chunk *pc); static void free_pv_entry(pmap_t pmap, pv_entry_t pv); static pv_entry_t get_pv_entry(pmap_t pmap, struct rwlock **lockp); static vm_page_t reclaim_pv_chunk(pmap_t locked_pmap, struct rwlock **lockp); static void pmap_pvh_free(struct md_page *pvh, pmap_t pmap, vm_offset_t va); static pv_entry_t pmap_pvh_remove(struct md_page *pvh, pmap_t pmap, vm_offset_t va); static vm_page_t pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, vm_page_t mpte, struct rwlock **lockp); static int pmap_remove_l3(pmap_t pmap, pt_entry_t *l3, vm_offset_t sva, pd_entry_t ptepde, struct spglist *free, struct rwlock **lockp); static boolean_t pmap_try_insert_pv_entry(pmap_t pmap, vm_offset_t va, vm_page_t m, struct rwlock **lockp); static vm_page_t _pmap_alloc_l3(pmap_t pmap, vm_pindex_t ptepindex, struct rwlock **lockp); static void _pmap_unwire_l3(pmap_t pmap, vm_offset_t va, vm_page_t m, struct spglist *free); static int pmap_unuse_l3(pmap_t, vm_offset_t, pd_entry_t, struct spglist *); /* * These load the old table data and store the new value. * They need to be atomic as the System MMU may write to the table at * the same time as the CPU. */ #define pmap_load_store(table, entry) atomic_swap_64(table, entry) #define pmap_set(table, mask) atomic_set_64(table, mask) #define pmap_load_clear(table) atomic_swap_64(table, 0) #define pmap_load(table) (*table) /********************/ /* Inline functions */ /********************/ static __inline void pagecopy(void *s, void *d) { memcpy(d, s, PAGE_SIZE); } static __inline void pagezero(void *p) { bzero(p, PAGE_SIZE); } #define pmap_l1_index(va) (((va) >> L1_SHIFT) & Ln_ADDR_MASK) #define pmap_l2_index(va) (((va) >> L2_SHIFT) & Ln_ADDR_MASK) #define pmap_l3_index(va) (((va) >> L3_SHIFT) & Ln_ADDR_MASK) #define PTE_TO_PHYS(pte) ((pte >> PTE_PPN0_S) * PAGE_SIZE) static __inline pd_entry_t * pmap_l1(pmap_t pmap, vm_offset_t va) { return (&pmap->pm_l1[pmap_l1_index(va)]); } static __inline pd_entry_t * pmap_l1_to_l2(pd_entry_t *l1, vm_offset_t va) { vm_paddr_t phys; pd_entry_t *l2; phys = PTE_TO_PHYS(pmap_load(l1)); l2 = (pd_entry_t *)PHYS_TO_DMAP(phys); return (&l2[pmap_l2_index(va)]); } static __inline pd_entry_t * pmap_l2(pmap_t pmap, vm_offset_t va) { pd_entry_t *l1; l1 = pmap_l1(pmap, va); if (l1 == NULL) return (NULL); if ((pmap_load(l1) & PTE_V) == 0) return (NULL); if ((pmap_load(l1) & PTE_RX) != 0) return (NULL); return (pmap_l1_to_l2(l1, va)); } static __inline pt_entry_t * pmap_l2_to_l3(pd_entry_t *l2, vm_offset_t va) { vm_paddr_t phys; pt_entry_t *l3; phys = PTE_TO_PHYS(pmap_load(l2)); l3 = (pd_entry_t *)PHYS_TO_DMAP(phys); return (&l3[pmap_l3_index(va)]); } static __inline pt_entry_t * pmap_l3(pmap_t pmap, vm_offset_t va) { pd_entry_t *l2; l2 = pmap_l2(pmap, va); if (l2 == NULL) return (NULL); if ((pmap_load(l2) & PTE_V) == 0) return (NULL); if ((pmap_load(l2) & PTE_RX) != 0) return (NULL); return (pmap_l2_to_l3(l2, va)); } - -static __inline int -pmap_is_write(pt_entry_t entry) -{ - - return (entry & PTE_W); -} - -static __inline int -pmap_l3_valid(pt_entry_t l3) -{ - - return (l3 & PTE_V); -} - -static inline int -pmap_page_accessed(pt_entry_t pte) -{ - - return (pte & PTE_A); -} - -/* Checks if the page is dirty. */ -static inline int -pmap_page_dirty(pt_entry_t pte) -{ - - return (pte & PTE_D); -} - static __inline void pmap_resident_count_inc(pmap_t pmap, int count) { PMAP_LOCK_ASSERT(pmap, MA_OWNED); pmap->pm_stats.resident_count += count; } static __inline void pmap_resident_count_dec(pmap_t pmap, int count) { PMAP_LOCK_ASSERT(pmap, MA_OWNED); KASSERT(pmap->pm_stats.resident_count >= count, ("pmap %p resident count underflow %ld %d", pmap, pmap->pm_stats.resident_count, count)); pmap->pm_stats.resident_count -= count; } static void pmap_distribute_l1(struct pmap *pmap, vm_pindex_t l1index, pt_entry_t entry) { struct pmap *user_pmap; pd_entry_t *l1; /* Distribute new kernel L1 entry to all the user pmaps */ if (pmap != kernel_pmap) return; LIST_FOREACH(user_pmap, &allpmaps, pm_list) { l1 = &user_pmap->pm_l1[l1index]; if (entry) pmap_load_store(l1, entry); else pmap_load_clear(l1); } } static pt_entry_t * pmap_early_page_idx(vm_offset_t l1pt, vm_offset_t va, u_int *l1_slot, u_int *l2_slot) { pt_entry_t *l2; pd_entry_t *l1; l1 = (pd_entry_t *)l1pt; *l1_slot = (va >> L1_SHIFT) & Ln_ADDR_MASK; /* Check locore has used a table L1 map */ KASSERT((l1[*l1_slot] & PTE_RX) == 0, ("Invalid bootstrap L1 table")); /* Find the address of the L2 table */ l2 = (pt_entry_t *)init_pt_va; *l2_slot = pmap_l2_index(va); return (l2); } static vm_paddr_t pmap_early_vtophys(vm_offset_t l1pt, vm_offset_t va) { u_int l1_slot, l2_slot; pt_entry_t *l2; u_int ret; l2 = pmap_early_page_idx(l1pt, va, &l1_slot, &l2_slot); /* Check locore has used L2 superpages */ KASSERT((l2[l2_slot] & PTE_RX) != 0, ("Invalid bootstrap L2 table")); /* L2 is superpages */ ret = (l2[l2_slot] >> PTE_PPN1_S) << L2_SHIFT; ret += (va & L2_OFFSET); return (ret); } static void pmap_bootstrap_dmap(vm_offset_t kern_l1, vm_paddr_t min_pa, vm_paddr_t max_pa) { vm_offset_t va; vm_paddr_t pa; pd_entry_t *l1; u_int l1_slot; pt_entry_t entry; pn_t pn; pa = dmap_phys_base = min_pa & ~L1_OFFSET; va = DMAP_MIN_ADDRESS; l1 = (pd_entry_t *)kern_l1; l1_slot = pmap_l1_index(DMAP_MIN_ADDRESS); for (; va < DMAP_MAX_ADDRESS && pa < max_pa; pa += L1_SIZE, va += L1_SIZE, l1_slot++) { KASSERT(l1_slot < Ln_ENTRIES, ("Invalid L1 index")); /* superpages */ pn = (pa / PAGE_SIZE); entry = PTE_KERN; entry |= (pn << PTE_PPN0_S); pmap_load_store(&l1[l1_slot], entry); } /* Set the upper limit of the DMAP region */ dmap_phys_max = pa; dmap_max_addr = va; sfence_vma(); } static vm_offset_t pmap_bootstrap_l3(vm_offset_t l1pt, vm_offset_t va, vm_offset_t l3_start) { vm_offset_t l3pt; pt_entry_t entry; pd_entry_t *l2; vm_paddr_t pa; u_int l2_slot; pn_t pn; KASSERT((va & L2_OFFSET) == 0, ("Invalid virtual address")); l2 = pmap_l2(kernel_pmap, va); l2 = (pd_entry_t *)((uintptr_t)l2 & ~(PAGE_SIZE - 1)); l2_slot = pmap_l2_index(va); l3pt = l3_start; for (; va < VM_MAX_KERNEL_ADDRESS; l2_slot++, va += L2_SIZE) { KASSERT(l2_slot < Ln_ENTRIES, ("Invalid L2 index")); pa = pmap_early_vtophys(l1pt, l3pt); pn = (pa / PAGE_SIZE); entry = (PTE_V); entry |= (pn << PTE_PPN0_S); pmap_load_store(&l2[l2_slot], entry); l3pt += PAGE_SIZE; } /* Clean the L2 page table */ memset((void *)l3_start, 0, l3pt - l3_start); return (l3pt); } /* * Bootstrap the system enough to run with virtual memory. */ void pmap_bootstrap(vm_offset_t l1pt, vm_paddr_t kernstart, vm_size_t kernlen) { u_int l1_slot, l2_slot, avail_slot, map_slot, used_map_slot; uint64_t kern_delta; pt_entry_t *l2; vm_offset_t va, freemempos; vm_offset_t dpcpu, msgbufpv; vm_paddr_t pa, min_pa, max_pa; int i; kern_delta = KERNBASE - kernstart; physmem = 0; printf("pmap_bootstrap %lx %lx %lx\n", l1pt, kernstart, kernlen); printf("%lx\n", l1pt); printf("%lx\n", (KERNBASE >> L1_SHIFT) & Ln_ADDR_MASK); /* Set this early so we can use the pagetable walking functions */ kernel_pmap_store.pm_l1 = (pd_entry_t *)l1pt; PMAP_LOCK_INIT(kernel_pmap); /* * Initialize the global pv list lock. */ rw_init(&pvh_global_lock, "pmap pv global"); LIST_INIT(&allpmaps); /* Assume the address we were loaded to is a valid physical address */ min_pa = max_pa = KERNBASE - kern_delta; /* * Find the minimum physical address. physmap is sorted, * but may contain empty ranges. */ for (i = 0; i < (physmap_idx * 2); i += 2) { if (physmap[i] == physmap[i + 1]) continue; if (physmap[i] <= min_pa) min_pa = physmap[i]; if (physmap[i + 1] > max_pa) max_pa = physmap[i + 1]; } printf("physmap_idx %lx\n", physmap_idx); printf("min_pa %lx\n", min_pa); printf("max_pa %lx\n", max_pa); /* Create a direct map region early so we can use it for pa -> va */ pmap_bootstrap_dmap(l1pt, min_pa, max_pa); va = KERNBASE; pa = KERNBASE - kern_delta; /* * Start to initialize phys_avail by copying from physmap * up to the physical address KERNBASE points at. */ map_slot = avail_slot = 0; for (; map_slot < (physmap_idx * 2); map_slot += 2) { if (physmap[map_slot] == physmap[map_slot + 1]) continue; if (physmap[map_slot] <= pa && physmap[map_slot + 1] > pa) break; phys_avail[avail_slot] = physmap[map_slot]; phys_avail[avail_slot + 1] = physmap[map_slot + 1]; physmem += (phys_avail[avail_slot + 1] - phys_avail[avail_slot]) >> PAGE_SHIFT; avail_slot += 2; } /* Add the memory before the kernel */ if (physmap[avail_slot] < pa) { phys_avail[avail_slot] = physmap[map_slot]; phys_avail[avail_slot + 1] = pa; physmem += (phys_avail[avail_slot + 1] - phys_avail[avail_slot]) >> PAGE_SHIFT; avail_slot += 2; } used_map_slot = map_slot; /* * Read the page table to find out what is already mapped. * This assumes we have mapped a block of memory from KERNBASE * using a single L1 entry. */ l2 = pmap_early_page_idx(l1pt, KERNBASE, &l1_slot, &l2_slot); /* Sanity check the index, KERNBASE should be the first VA */ KASSERT(l2_slot == 0, ("The L2 index is non-zero")); /* Find how many pages we have mapped */ for (; l2_slot < Ln_ENTRIES; l2_slot++) { if ((l2[l2_slot] & PTE_V) == 0) break; /* Check locore used L2 superpages */ KASSERT((l2[l2_slot] & PTE_RX) != 0, ("Invalid bootstrap L2 table")); va += L2_SIZE; pa += L2_SIZE; } va = roundup2(va, L2_SIZE); freemempos = KERNBASE + kernlen; freemempos = roundup2(freemempos, PAGE_SIZE); /* Create the l3 tables for the early devmap */ freemempos = pmap_bootstrap_l3(l1pt, VM_MAX_KERNEL_ADDRESS - L2_SIZE, freemempos); sfence_vma(); #define alloc_pages(var, np) \ (var) = freemempos; \ freemempos += (np * PAGE_SIZE); \ memset((char *)(var), 0, ((np) * PAGE_SIZE)); /* Allocate dynamic per-cpu area. */ alloc_pages(dpcpu, DPCPU_SIZE / PAGE_SIZE); dpcpu_init((void *)dpcpu, 0); /* Allocate memory for the msgbuf, e.g. for /sbin/dmesg */ alloc_pages(msgbufpv, round_page(msgbufsize) / PAGE_SIZE); msgbufp = (void *)msgbufpv; virtual_avail = roundup2(freemempos, L2_SIZE); virtual_end = VM_MAX_KERNEL_ADDRESS - L2_SIZE; kernel_vm_end = virtual_avail; pa = pmap_early_vtophys(l1pt, freemempos); /* Finish initialising physmap */ map_slot = used_map_slot; for (; avail_slot < (PHYS_AVAIL_SIZE - 2) && map_slot < (physmap_idx * 2); map_slot += 2) { if (physmap[map_slot] == physmap[map_slot + 1]) { continue; } /* Have we used the current range? */ if (physmap[map_slot + 1] <= pa) { continue; } /* Do we need to split the entry? */ if (physmap[map_slot] < pa) { phys_avail[avail_slot] = pa; phys_avail[avail_slot + 1] = physmap[map_slot + 1]; } else { phys_avail[avail_slot] = physmap[map_slot]; phys_avail[avail_slot + 1] = physmap[map_slot + 1]; } physmem += (phys_avail[avail_slot + 1] - phys_avail[avail_slot]) >> PAGE_SHIFT; avail_slot += 2; } phys_avail[avail_slot] = 0; phys_avail[avail_slot + 1] = 0; /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". */ Maxmem = atop(phys_avail[avail_slot - 1]); } /* * Initialize a vm_page's machine-dependent fields. */ void pmap_page_init(vm_page_t m) { TAILQ_INIT(&m->md.pv_list); m->md.pv_memattr = VM_MEMATTR_WRITE_BACK; } /* * Initialize the pmap module. * Called by vm_init, to initialize any structures that the pmap * system needs to map virtual memory. */ void pmap_init(void) { int i; /* * Initialize the pv chunk list mutex. */ mtx_init(&pv_chunks_mutex, "pmap pv chunk list", NULL, MTX_DEF); /* * Initialize the pool of pv list locks. */ for (i = 0; i < NPV_LIST_LOCKS; i++) rw_init(&pv_list_locks[i], "pmap pv list"); } #ifdef SMP /* * For SMP, these functions have to use IPIs for coherence. * * In general, the calling thread uses a plain fence to order the * writes to the page tables before invoking an SBI callback to invoke * sfence_vma() on remote CPUs. * * Since the riscv pmap does not yet have a pm_active field, IPIs are * sent to all CPUs in the system. */ static void pmap_invalidate_page(pmap_t pmap, vm_offset_t va) { cpuset_t mask; sched_pin(); mask = all_cpus; CPU_CLR(PCPU_GET(cpuid), &mask); fence(); sbi_remote_sfence_vma(mask.__bits, va, 1); sfence_vma_page(va); sched_unpin(); } static void pmap_invalidate_range(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { cpuset_t mask; sched_pin(); mask = all_cpus; CPU_CLR(PCPU_GET(cpuid), &mask); fence(); sbi_remote_sfence_vma(mask.__bits, sva, eva - sva + 1); /* * Might consider a loop of sfence_vma_page() for a small * number of pages in the future. */ sfence_vma(); sched_unpin(); } static void pmap_invalidate_all(pmap_t pmap) { cpuset_t mask; sched_pin(); mask = all_cpus; CPU_CLR(PCPU_GET(cpuid), &mask); fence(); /* * XXX: The SBI doc doesn't detail how to specify x0 as the * address to perform a global fence. BBL currently treats * all sfence_vma requests as global however. */ sbi_remote_sfence_vma(mask.__bits, 0, 0); sfence_vma(); sched_unpin(); } #else /* * Normal, non-SMP, invalidation functions. * We inline these within pmap.c for speed. */ static __inline void pmap_invalidate_page(pmap_t pmap, vm_offset_t va) { sfence_vma_page(va); } static __inline void pmap_invalidate_range(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { /* * Might consider a loop of sfence_vma_page() for a small * number of pages in the future. */ sfence_vma(); } static __inline void pmap_invalidate_all(pmap_t pmap) { sfence_vma(); } #endif /* * Routine: pmap_extract * Function: * Extract the physical page address associated * with the given map/virtual_address pair. */ vm_paddr_t pmap_extract(pmap_t pmap, vm_offset_t va) { pd_entry_t *l2p, l2; pt_entry_t *l3p, l3; vm_paddr_t pa; pa = 0; PMAP_LOCK(pmap); /* * Start with the l2 tabel. We are unable to allocate * pages in the l1 table. */ l2p = pmap_l2(pmap, va); if (l2p != NULL) { l2 = pmap_load(l2p); if ((l2 & PTE_RX) == 0) { l3p = pmap_l2_to_l3(l2p, va); if (l3p != NULL) { l3 = pmap_load(l3p); pa = PTE_TO_PHYS(l3); pa |= (va & L3_OFFSET); } } else { /* L2 is superpages */ pa = (l2 >> PTE_PPN1_S) << L2_SHIFT; pa |= (va & L2_OFFSET); } } PMAP_UNLOCK(pmap); return (pa); } /* * Routine: pmap_extract_and_hold * Function: * Atomically extract and hold the physical page * with the given pmap and virtual address pair * if that mapping permits the given protection. */ vm_page_t pmap_extract_and_hold(pmap_t pmap, vm_offset_t va, vm_prot_t prot) { pt_entry_t *l3p, l3; vm_paddr_t phys; vm_paddr_t pa; vm_page_t m; pa = 0; m = NULL; PMAP_LOCK(pmap); retry: l3p = pmap_l3(pmap, va); if (l3p != NULL && (l3 = pmap_load(l3p)) != 0) { - if ((pmap_is_write(l3)) || ((prot & VM_PROT_WRITE) == 0)) { + if ((l3 & PTE_W) != 0 || (prot & VM_PROT_WRITE) == 0) { phys = PTE_TO_PHYS(l3); if (vm_page_pa_tryrelock(pmap, phys, &pa)) goto retry; m = PHYS_TO_VM_PAGE(phys); vm_page_hold(m); } } PA_UNLOCK_COND(pa); PMAP_UNLOCK(pmap); return (m); } vm_paddr_t pmap_kextract(vm_offset_t va) { pd_entry_t *l2; pt_entry_t *l3; vm_paddr_t pa; if (va >= DMAP_MIN_ADDRESS && va < DMAP_MAX_ADDRESS) { pa = DMAP_TO_PHYS(va); } else { l2 = pmap_l2(kernel_pmap, va); if (l2 == NULL) panic("pmap_kextract: No l2"); if ((pmap_load(l2) & PTE_RX) != 0) { /* superpages */ pa = (pmap_load(l2) >> PTE_PPN1_S) << L2_SHIFT; pa |= (va & L2_OFFSET); return (pa); } l3 = pmap_l2_to_l3(l2, va); if (l3 == NULL) panic("pmap_kextract: No l3..."); pa = PTE_TO_PHYS(pmap_load(l3)); pa |= (va & PAGE_MASK); } return (pa); } /*************************************************** * Low level mapping routines..... ***************************************************/ void pmap_kenter_device(vm_offset_t sva, vm_size_t size, vm_paddr_t pa) { pt_entry_t entry; pt_entry_t *l3; vm_offset_t va; pn_t pn; KASSERT((pa & L3_OFFSET) == 0, ("pmap_kenter_device: Invalid physical address")); KASSERT((sva & L3_OFFSET) == 0, ("pmap_kenter_device: Invalid virtual address")); KASSERT((size & PAGE_MASK) == 0, ("pmap_kenter_device: Mapping is not page-sized")); va = sva; while (size != 0) { l3 = pmap_l3(kernel_pmap, va); KASSERT(l3 != NULL, ("Invalid page table, va: 0x%lx", va)); pn = (pa / PAGE_SIZE); entry = PTE_KERN; entry |= (pn << PTE_PPN0_S); pmap_load_store(l3, entry); va += PAGE_SIZE; pa += PAGE_SIZE; size -= PAGE_SIZE; } pmap_invalidate_range(kernel_pmap, sva, va); } /* * Remove a page from the kernel pagetables. * Note: not SMP coherent. */ PMAP_INLINE void pmap_kremove(vm_offset_t va) { pt_entry_t *l3; l3 = pmap_l3(kernel_pmap, va); KASSERT(l3 != NULL, ("pmap_kremove: Invalid address")); pmap_load_clear(l3); sfence_vma(); } void pmap_kremove_device(vm_offset_t sva, vm_size_t size) { pt_entry_t *l3; vm_offset_t va; KASSERT((sva & L3_OFFSET) == 0, ("pmap_kremove_device: Invalid virtual address")); KASSERT((size & PAGE_MASK) == 0, ("pmap_kremove_device: Mapping is not page-sized")); va = sva; while (size != 0) { l3 = pmap_l3(kernel_pmap, va); KASSERT(l3 != NULL, ("Invalid page table, va: 0x%lx", va)); pmap_load_clear(l3); va += PAGE_SIZE; size -= PAGE_SIZE; } pmap_invalidate_range(kernel_pmap, sva, va); } /* * Used to map a range of physical addresses into kernel * virtual address space. * * The value passed in '*virt' is a suggested virtual address for * the mapping. Architectures which can support a direct-mapped * physical to virtual region can return the appropriate address * within that region, leaving '*virt' unchanged. Other * architectures should map the pages starting at '*virt' and * update '*virt' with the first usable address after the mapped * region. */ vm_offset_t pmap_map(vm_offset_t *virt, vm_paddr_t start, vm_paddr_t end, int prot) { return PHYS_TO_DMAP(start); } /* * Add a list of wired pages to the kva * this routine is only used for temporary * kernel mappings that do not need to have * page modification or references recorded. * Note that old mappings are simply written * over. The page *must* be wired. * Note: SMP coherent. Uses a ranged shootdown IPI. */ void pmap_qenter(vm_offset_t sva, vm_page_t *ma, int count) { pt_entry_t *l3, pa; vm_offset_t va; vm_page_t m; pt_entry_t entry; pn_t pn; int i; va = sva; for (i = 0; i < count; i++) { m = ma[i]; pa = VM_PAGE_TO_PHYS(m); pn = (pa / PAGE_SIZE); l3 = pmap_l3(kernel_pmap, va); entry = PTE_KERN; entry |= (pn << PTE_PPN0_S); pmap_load_store(l3, entry); va += L3_SIZE; } pmap_invalidate_range(kernel_pmap, sva, va); } /* * This routine tears out page mappings from the * kernel -- it is meant only for temporary mappings. * Note: SMP coherent. Uses a ranged shootdown IPI. */ void pmap_qremove(vm_offset_t sva, int count) { pt_entry_t *l3; vm_offset_t va; KASSERT(sva >= VM_MIN_KERNEL_ADDRESS, ("usermode va %lx", sva)); va = sva; while (count-- > 0) { l3 = pmap_l3(kernel_pmap, va); KASSERT(l3 != NULL, ("pmap_kremove: Invalid address")); pmap_load_clear(l3); va += PAGE_SIZE; } pmap_invalidate_range(kernel_pmap, sva, va); } /*************************************************** * Page table page management routines..... ***************************************************/ /* * Schedule the specified unused page table page to be freed. Specifically, * add the page to the specified list of pages that will be released to the * physical memory manager after the TLB has been updated. */ static __inline void pmap_add_delayed_free_list(vm_page_t m, struct spglist *free, boolean_t set_PG_ZERO) { if (set_PG_ZERO) m->flags |= PG_ZERO; else m->flags &= ~PG_ZERO; SLIST_INSERT_HEAD(free, m, plinks.s.ss); } /* * Decrements a page table page's wire count, which is used to record the * number of valid page table entries within the page. If the wire count * drops to zero, then the page table page is unmapped. Returns TRUE if the * page table page was unmapped and FALSE otherwise. */ static inline boolean_t pmap_unwire_l3(pmap_t pmap, vm_offset_t va, vm_page_t m, struct spglist *free) { --m->wire_count; if (m->wire_count == 0) { _pmap_unwire_l3(pmap, va, m, free); return (TRUE); } else { return (FALSE); } } static void _pmap_unwire_l3(pmap_t pmap, vm_offset_t va, vm_page_t m, struct spglist *free) { vm_paddr_t phys; PMAP_LOCK_ASSERT(pmap, MA_OWNED); /* * unmap the page table page */ if (m->pindex >= NUPDE) { /* PD page */ pd_entry_t *l1; l1 = pmap_l1(pmap, va); pmap_load_clear(l1); pmap_distribute_l1(pmap, pmap_l1_index(va), 0); } else { /* PTE page */ pd_entry_t *l2; l2 = pmap_l2(pmap, va); pmap_load_clear(l2); } pmap_resident_count_dec(pmap, 1); if (m->pindex < NUPDE) { pd_entry_t *l1; /* We just released a PT, unhold the matching PD */ vm_page_t pdpg; l1 = pmap_l1(pmap, va); phys = PTE_TO_PHYS(pmap_load(l1)); pdpg = PHYS_TO_VM_PAGE(phys); pmap_unwire_l3(pmap, va, pdpg, free); } pmap_invalidate_page(pmap, va); vm_wire_sub(1); /* * Put page on a list so that it is released after * *ALL* TLB shootdown is done */ pmap_add_delayed_free_list(m, free, TRUE); } /* * After removing an l3 entry, this routine is used to * conditionally free the page, and manage the hold/wire counts. */ static int pmap_unuse_l3(pmap_t pmap, vm_offset_t va, pd_entry_t ptepde, struct spglist *free) { vm_paddr_t phys; vm_page_t mpte; if (va >= VM_MAXUSER_ADDRESS) return (0); KASSERT(ptepde != 0, ("pmap_unuse_pt: ptepde != 0")); phys = PTE_TO_PHYS(ptepde); mpte = PHYS_TO_VM_PAGE(phys); return (pmap_unwire_l3(pmap, va, mpte, free)); } void pmap_pinit0(pmap_t pmap) { PMAP_LOCK_INIT(pmap); bzero(&pmap->pm_stats, sizeof(pmap->pm_stats)); pmap->pm_l1 = kernel_pmap->pm_l1; } int pmap_pinit(pmap_t pmap) { vm_paddr_t l1phys; vm_page_t l1pt; /* * allocate the l1 page */ while ((l1pt = vm_page_alloc(NULL, 0xdeadbeef, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO)) == NULL) vm_wait(NULL); l1phys = VM_PAGE_TO_PHYS(l1pt); pmap->pm_l1 = (pd_entry_t *)PHYS_TO_DMAP(l1phys); if ((l1pt->flags & PG_ZERO) == 0) pagezero(pmap->pm_l1); bzero(&pmap->pm_stats, sizeof(pmap->pm_stats)); /* Install kernel pagetables */ memcpy(pmap->pm_l1, kernel_pmap->pm_l1, PAGE_SIZE); /* Add to the list of all user pmaps */ LIST_INSERT_HEAD(&allpmaps, pmap, pm_list); return (1); } /* * This routine is called if the desired page table page does not exist. * * If page table page allocation fails, this routine may sleep before * returning NULL. It sleeps only if a lock pointer was given. * * Note: If a page allocation fails at page table level two or three, * one or two pages may be held during the wait, only to be released * afterwards. This conservative approach is easily argued to avoid * race conditions. */ static vm_page_t _pmap_alloc_l3(pmap_t pmap, vm_pindex_t ptepindex, struct rwlock **lockp) { vm_page_t m, /*pdppg, */pdpg; pt_entry_t entry; vm_paddr_t phys; pn_t pn; PMAP_LOCK_ASSERT(pmap, MA_OWNED); /* * Allocate a page table page. */ if ((m = vm_page_alloc(NULL, ptepindex, VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO)) == NULL) { if (lockp != NULL) { RELEASE_PV_LIST_LOCK(lockp); PMAP_UNLOCK(pmap); rw_runlock(&pvh_global_lock); vm_wait(NULL); rw_rlock(&pvh_global_lock); PMAP_LOCK(pmap); } /* * Indicate the need to retry. While waiting, the page table * page may have been allocated. */ return (NULL); } if ((m->flags & PG_ZERO) == 0) pmap_zero_page(m); /* * Map the pagetable page into the process address space, if * it isn't already there. */ if (ptepindex >= NUPDE) { pd_entry_t *l1; vm_pindex_t l1index; l1index = ptepindex - NUPDE; l1 = &pmap->pm_l1[l1index]; pn = (VM_PAGE_TO_PHYS(m) / PAGE_SIZE); entry = (PTE_V); entry |= (pn << PTE_PPN0_S); pmap_load_store(l1, entry); pmap_distribute_l1(pmap, l1index, entry); } else { vm_pindex_t l1index; pd_entry_t *l1, *l2; l1index = ptepindex >> (L1_SHIFT - L2_SHIFT); l1 = &pmap->pm_l1[l1index]; if (pmap_load(l1) == 0) { /* recurse for allocating page dir */ if (_pmap_alloc_l3(pmap, NUPDE + l1index, lockp) == NULL) { vm_page_unwire_noq(m); vm_page_free_zero(m); return (NULL); } } else { phys = PTE_TO_PHYS(pmap_load(l1)); pdpg = PHYS_TO_VM_PAGE(phys); pdpg->wire_count++; } phys = PTE_TO_PHYS(pmap_load(l1)); l2 = (pd_entry_t *)PHYS_TO_DMAP(phys); l2 = &l2[ptepindex & Ln_ADDR_MASK]; pn = (VM_PAGE_TO_PHYS(m) / PAGE_SIZE); entry = (PTE_V); entry |= (pn << PTE_PPN0_S); pmap_load_store(l2, entry); } pmap_resident_count_inc(pmap, 1); return (m); } static vm_page_t pmap_alloc_l3(pmap_t pmap, vm_offset_t va, struct rwlock **lockp) { vm_pindex_t ptepindex; pd_entry_t *l2; vm_paddr_t phys; vm_page_t m; /* * Calculate pagetable page index */ ptepindex = pmap_l2_pindex(va); retry: /* * Get the page directory entry */ l2 = pmap_l2(pmap, va); /* * If the page table page is mapped, we just increment the * hold count, and activate it. */ if (l2 != NULL && pmap_load(l2) != 0) { phys = PTE_TO_PHYS(pmap_load(l2)); m = PHYS_TO_VM_PAGE(phys); m->wire_count++; } else { /* * Here if the pte page isn't mapped, or if it has been * deallocated. */ m = _pmap_alloc_l3(pmap, ptepindex, lockp); if (m == NULL && lockp != NULL) goto retry; } return (m); } /*************************************************** * Pmap allocation/deallocation routines. ***************************************************/ /* * Release any resources held by the given physical map. * Called when a pmap initialized by pmap_pinit is being released. * Should only be called if the map contains no valid mappings. */ void pmap_release(pmap_t pmap) { vm_page_t m; KASSERT(pmap->pm_stats.resident_count == 0, ("pmap_release: pmap resident count %ld != 0", pmap->pm_stats.resident_count)); m = PHYS_TO_VM_PAGE(DMAP_TO_PHYS((vm_offset_t)pmap->pm_l1)); vm_page_unwire_noq(m); vm_page_free_zero(m); /* Remove pmap from the allpmaps list */ LIST_REMOVE(pmap, pm_list); /* Remove kernel pagetables */ bzero(pmap->pm_l1, PAGE_SIZE); } #if 0 static int kvm_size(SYSCTL_HANDLER_ARGS) { unsigned long ksize = VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS; return sysctl_handle_long(oidp, &ksize, 0, req); } SYSCTL_PROC(_vm, OID_AUTO, kvm_size, CTLTYPE_LONG|CTLFLAG_RD, 0, 0, kvm_size, "LU", "Size of KVM"); static int kvm_free(SYSCTL_HANDLER_ARGS) { unsigned long kfree = VM_MAX_KERNEL_ADDRESS - kernel_vm_end; return sysctl_handle_long(oidp, &kfree, 0, req); } SYSCTL_PROC(_vm, OID_AUTO, kvm_free, CTLTYPE_LONG|CTLFLAG_RD, 0, 0, kvm_free, "LU", "Amount of KVM free"); #endif /* 0 */ /* * grow the number of kernel page table entries, if needed */ void pmap_growkernel(vm_offset_t addr) { vm_paddr_t paddr; vm_page_t nkpg; pd_entry_t *l1, *l2; pt_entry_t entry; pn_t pn; mtx_assert(&kernel_map->system_mtx, MA_OWNED); addr = roundup2(addr, L2_SIZE); if (addr - 1 >= vm_map_max(kernel_map)) addr = vm_map_max(kernel_map); while (kernel_vm_end < addr) { l1 = pmap_l1(kernel_pmap, kernel_vm_end); if (pmap_load(l1) == 0) { /* We need a new PDP entry */ nkpg = vm_page_alloc(NULL, kernel_vm_end >> L1_SHIFT, VM_ALLOC_INTERRUPT | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO); if (nkpg == NULL) panic("pmap_growkernel: no memory to grow kernel"); if ((nkpg->flags & PG_ZERO) == 0) pmap_zero_page(nkpg); paddr = VM_PAGE_TO_PHYS(nkpg); pn = (paddr / PAGE_SIZE); entry = (PTE_V); entry |= (pn << PTE_PPN0_S); pmap_load_store(l1, entry); pmap_distribute_l1(kernel_pmap, pmap_l1_index(kernel_vm_end), entry); continue; /* try again */ } l2 = pmap_l1_to_l2(l1, kernel_vm_end); if ((pmap_load(l2) & PTE_V) != 0 && (pmap_load(l2) & PTE_RWX) == 0) { kernel_vm_end = (kernel_vm_end + L2_SIZE) & ~L2_OFFSET; if (kernel_vm_end - 1 >= vm_map_max(kernel_map)) { kernel_vm_end = vm_map_max(kernel_map); break; } continue; } nkpg = vm_page_alloc(NULL, kernel_vm_end >> L2_SHIFT, VM_ALLOC_INTERRUPT | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO); if (nkpg == NULL) panic("pmap_growkernel: no memory to grow kernel"); if ((nkpg->flags & PG_ZERO) == 0) { pmap_zero_page(nkpg); } paddr = VM_PAGE_TO_PHYS(nkpg); pn = (paddr / PAGE_SIZE); entry = (PTE_V); entry |= (pn << PTE_PPN0_S); pmap_load_store(l2, entry); pmap_invalidate_page(kernel_pmap, kernel_vm_end); kernel_vm_end = (kernel_vm_end + L2_SIZE) & ~L2_OFFSET; if (kernel_vm_end - 1 >= vm_map_max(kernel_map)) { kernel_vm_end = vm_map_max(kernel_map); break; } } } /*************************************************** * page management routines. ***************************************************/ CTASSERT(sizeof(struct pv_chunk) == PAGE_SIZE); CTASSERT(_NPCM == 3); CTASSERT(_NPCPV == 168); static __inline struct pv_chunk * pv_to_chunk(pv_entry_t pv) { return ((struct pv_chunk *)((uintptr_t)pv & ~(uintptr_t)PAGE_MASK)); } #define PV_PMAP(pv) (pv_to_chunk(pv)->pc_pmap) #define PC_FREE0 0xfffffffffffffffful #define PC_FREE1 0xfffffffffffffffful #define PC_FREE2 0x000000fffffffffful static const uint64_t pc_freemask[_NPCM] = { PC_FREE0, PC_FREE1, PC_FREE2 }; #if 0 #ifdef PV_STATS static int pc_chunk_count, pc_chunk_allocs, pc_chunk_frees, pc_chunk_tryfail; SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_count, CTLFLAG_RD, &pc_chunk_count, 0, "Current number of pv entry chunks"); SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_allocs, CTLFLAG_RD, &pc_chunk_allocs, 0, "Current number of pv entry chunks allocated"); SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_frees, CTLFLAG_RD, &pc_chunk_frees, 0, "Current number of pv entry chunks frees"); SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_tryfail, CTLFLAG_RD, &pc_chunk_tryfail, 0, "Number of times tried to get a chunk page but failed."); static long pv_entry_frees, pv_entry_allocs, pv_entry_count; static int pv_entry_spare; SYSCTL_LONG(_vm_pmap, OID_AUTO, pv_entry_frees, CTLFLAG_RD, &pv_entry_frees, 0, "Current number of pv entry frees"); SYSCTL_LONG(_vm_pmap, OID_AUTO, pv_entry_allocs, CTLFLAG_RD, &pv_entry_allocs, 0, "Current number of pv entry allocs"); SYSCTL_LONG(_vm_pmap, OID_AUTO, pv_entry_count, CTLFLAG_RD, &pv_entry_count, 0, "Current number of pv entries"); SYSCTL_INT(_vm_pmap, OID_AUTO, pv_entry_spare, CTLFLAG_RD, &pv_entry_spare, 0, "Current number of spare pv entries"); #endif #endif /* 0 */ /* * We are in a serious low memory condition. Resort to * drastic measures to free some pages so we can allocate * another pv entry chunk. * * Returns NULL if PV entries were reclaimed from the specified pmap. * * We do not, however, unmap 2mpages because subsequent accesses will * allocate per-page pv entries until repromotion occurs, thereby * exacerbating the shortage of free pv entries. */ static vm_page_t reclaim_pv_chunk(pmap_t locked_pmap, struct rwlock **lockp) { panic("RISCVTODO: reclaim_pv_chunk"); } /* * free the pv_entry back to the free list */ static void free_pv_entry(pmap_t pmap, pv_entry_t pv) { struct pv_chunk *pc; int idx, field, bit; rw_assert(&pvh_global_lock, RA_LOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); PV_STAT(atomic_add_long(&pv_entry_frees, 1)); PV_STAT(atomic_add_int(&pv_entry_spare, 1)); PV_STAT(atomic_subtract_long(&pv_entry_count, 1)); pc = pv_to_chunk(pv); idx = pv - &pc->pc_pventry[0]; field = idx / 64; bit = idx % 64; pc->pc_map[field] |= 1ul << bit; if (pc->pc_map[0] != PC_FREE0 || pc->pc_map[1] != PC_FREE1 || pc->pc_map[2] != PC_FREE2) { /* 98% of the time, pc is already at the head of the list. */ if (__predict_false(pc != TAILQ_FIRST(&pmap->pm_pvchunk))) { TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc, pc_list); } return; } TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); free_pv_chunk(pc); } static void free_pv_chunk(struct pv_chunk *pc) { vm_page_t m; mtx_lock(&pv_chunks_mutex); TAILQ_REMOVE(&pv_chunks, pc, pc_lru); mtx_unlock(&pv_chunks_mutex); PV_STAT(atomic_subtract_int(&pv_entry_spare, _NPCPV)); PV_STAT(atomic_subtract_int(&pc_chunk_count, 1)); PV_STAT(atomic_add_int(&pc_chunk_frees, 1)); /* entire chunk is free, return it */ m = PHYS_TO_VM_PAGE(DMAP_TO_PHYS((vm_offset_t)pc)); #if 0 /* TODO: For minidump */ dump_drop_page(m->phys_addr); #endif vm_page_unwire(m, PQ_NONE); vm_page_free(m); } /* * Returns a new PV entry, allocating a new PV chunk from the system when * needed. If this PV chunk allocation fails and a PV list lock pointer was * given, a PV chunk is reclaimed from an arbitrary pmap. Otherwise, NULL is * returned. * * The given PV list lock may be released. */ static pv_entry_t get_pv_entry(pmap_t pmap, struct rwlock **lockp) { int bit, field; pv_entry_t pv; struct pv_chunk *pc; vm_page_t m; rw_assert(&pvh_global_lock, RA_LOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); PV_STAT(atomic_add_long(&pv_entry_allocs, 1)); retry: pc = TAILQ_FIRST(&pmap->pm_pvchunk); if (pc != NULL) { for (field = 0; field < _NPCM; field++) { if (pc->pc_map[field]) { bit = ffsl(pc->pc_map[field]) - 1; break; } } if (field < _NPCM) { pv = &pc->pc_pventry[field * 64 + bit]; pc->pc_map[field] &= ~(1ul << bit); /* If this was the last item, move it to tail */ if (pc->pc_map[0] == 0 && pc->pc_map[1] == 0 && pc->pc_map[2] == 0) { TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); TAILQ_INSERT_TAIL(&pmap->pm_pvchunk, pc, pc_list); } PV_STAT(atomic_add_long(&pv_entry_count, 1)); PV_STAT(atomic_subtract_int(&pv_entry_spare, 1)); return (pv); } } /* No free items, allocate another chunk */ m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED); if (m == NULL) { if (lockp == NULL) { PV_STAT(pc_chunk_tryfail++); return (NULL); } m = reclaim_pv_chunk(pmap, lockp); if (m == NULL) goto retry; } PV_STAT(atomic_add_int(&pc_chunk_count, 1)); PV_STAT(atomic_add_int(&pc_chunk_allocs, 1)); #if 0 /* TODO: This is for minidump */ dump_add_page(m->phys_addr); #endif pc = (void *)PHYS_TO_DMAP(m->phys_addr); pc->pc_pmap = pmap; pc->pc_map[0] = PC_FREE0 & ~1ul; /* preallocated bit 0 */ pc->pc_map[1] = PC_FREE1; pc->pc_map[2] = PC_FREE2; mtx_lock(&pv_chunks_mutex); TAILQ_INSERT_TAIL(&pv_chunks, pc, pc_lru); mtx_unlock(&pv_chunks_mutex); pv = &pc->pc_pventry[0]; TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc, pc_list); PV_STAT(atomic_add_long(&pv_entry_count, 1)); PV_STAT(atomic_add_int(&pv_entry_spare, _NPCPV - 1)); return (pv); } /* * First find and then remove the pv entry for the specified pmap and virtual * address from the specified pv list. Returns the pv entry if found and NULL * otherwise. This operation can be performed on pv lists for either 4KB or * 2MB page mappings. */ static __inline pv_entry_t pmap_pvh_remove(struct md_page *pvh, pmap_t pmap, vm_offset_t va) { pv_entry_t pv; rw_assert(&pvh_global_lock, RA_LOCKED); TAILQ_FOREACH(pv, &pvh->pv_list, pv_next) { if (pmap == PV_PMAP(pv) && va == pv->pv_va) { TAILQ_REMOVE(&pvh->pv_list, pv, pv_next); pvh->pv_gen++; break; } } return (pv); } /* * First find and then destroy the pv entry for the specified pmap and virtual * address. This operation can be performed on pv lists for either 4KB or 2MB * page mappings. */ static void pmap_pvh_free(struct md_page *pvh, pmap_t pmap, vm_offset_t va) { pv_entry_t pv; pv = pmap_pvh_remove(pvh, pmap, va); KASSERT(pv != NULL, ("pmap_pvh_free: pv not found")); free_pv_entry(pmap, pv); } /* * Conditionally create the PV entry for a 4KB page mapping if the required * memory can be allocated without resorting to reclamation. */ static boolean_t pmap_try_insert_pv_entry(pmap_t pmap, vm_offset_t va, vm_page_t m, struct rwlock **lockp) { pv_entry_t pv; rw_assert(&pvh_global_lock, RA_LOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); /* Pass NULL instead of the lock pointer to disable reclamation. */ if ((pv = get_pv_entry(pmap, NULL)) != NULL) { pv->pv_va = va; CHANGE_PV_LIST_LOCK_TO_VM_PAGE(lockp, m); TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next); m->md.pv_gen++; return (TRUE); } else return (FALSE); } /* * pmap_remove_l3: do the things to unmap a page in a process */ static int pmap_remove_l3(pmap_t pmap, pt_entry_t *l3, vm_offset_t va, pd_entry_t l2e, struct spglist *free, struct rwlock **lockp) { pt_entry_t old_l3; vm_paddr_t phys; vm_page_t m; PMAP_LOCK_ASSERT(pmap, MA_OWNED); old_l3 = pmap_load_clear(l3); pmap_invalidate_page(pmap, va); if (old_l3 & PTE_SW_WIRED) pmap->pm_stats.wired_count -= 1; pmap_resident_count_dec(pmap, 1); if (old_l3 & PTE_SW_MANAGED) { phys = PTE_TO_PHYS(old_l3); m = PHYS_TO_VM_PAGE(phys); - if (pmap_page_dirty(old_l3)) + if ((old_l3 & PTE_D) != 0) vm_page_dirty(m); if (old_l3 & PTE_A) vm_page_aflag_set(m, PGA_REFERENCED); CHANGE_PV_LIST_LOCK_TO_VM_PAGE(lockp, m); pmap_pvh_free(&m->md, pmap, va); } return (pmap_unuse_l3(pmap, va, l2e, free)); } /* * Remove the given range of addresses from the specified map. * * It is assumed that the start and end are properly * rounded to the page size. */ void pmap_remove(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { struct rwlock *lock; vm_offset_t va, va_next; pd_entry_t *l1, *l2; pt_entry_t l3_pte, *l3; struct spglist free; /* * Perform an unsynchronized read. This is, however, safe. */ if (pmap->pm_stats.resident_count == 0) return; SLIST_INIT(&free); rw_rlock(&pvh_global_lock); PMAP_LOCK(pmap); lock = NULL; for (; sva < eva; sva = va_next) { if (pmap->pm_stats.resident_count == 0) break; l1 = pmap_l1(pmap, sva); if (pmap_load(l1) == 0) { va_next = (sva + L1_SIZE) & ~L1_OFFSET; if (va_next < sva) va_next = eva; continue; } /* * Calculate index for next page table. */ va_next = (sva + L2_SIZE) & ~L2_OFFSET; if (va_next < sva) va_next = eva; l2 = pmap_l1_to_l2(l1, sva); if (l2 == NULL) continue; l3_pte = pmap_load(l2); /* * Weed out invalid mappings. */ if (l3_pte == 0) continue; if ((pmap_load(l2) & PTE_RX) != 0) continue; /* * Limit our scan to either the end of the va represented * by the current page table page, or to the end of the * range being removed. */ if (va_next > eva) va_next = eva; va = va_next; for (l3 = pmap_l2_to_l3(l2, sva); sva != va_next; l3++, sva += L3_SIZE) { if (l3 == NULL) panic("l3 == NULL"); if (pmap_load(l3) == 0) { if (va != va_next) { pmap_invalidate_range(pmap, va, sva); va = va_next; } continue; } if (va == va_next) va = sva; if (pmap_remove_l3(pmap, l3, sva, l3_pte, &free, &lock)) { sva += L3_SIZE; break; } } if (va != va_next) pmap_invalidate_range(pmap, va, sva); } if (lock != NULL) rw_wunlock(lock); rw_runlock(&pvh_global_lock); PMAP_UNLOCK(pmap); vm_page_free_pages_toq(&free, false); } /* * Routine: pmap_remove_all * Function: * Removes this physical page from * all physical maps in which it resides. * Reflects back modify bits to the pager. * * Notes: * Original versions of this routine were very * inefficient because they iteratively called * pmap_remove (slow...) */ void pmap_remove_all(vm_page_t m) { pv_entry_t pv; pmap_t pmap; pt_entry_t *l3, tl3; pd_entry_t *l2, tl2; struct spglist free; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_all: page %p is not managed", m)); SLIST_INIT(&free); rw_wlock(&pvh_global_lock); while ((pv = TAILQ_FIRST(&m->md.pv_list)) != NULL) { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); pmap_resident_count_dec(pmap, 1); l2 = pmap_l2(pmap, pv->pv_va); KASSERT(l2 != NULL, ("pmap_remove_all: no l2 table found")); tl2 = pmap_load(l2); KASSERT((tl2 & PTE_RX) == 0, ("pmap_remove_all: found a table when expecting " "a block in %p's pv list", m)); l3 = pmap_l2_to_l3(l2, pv->pv_va); tl3 = pmap_load_clear(l3); pmap_invalidate_page(pmap, pv->pv_va); if (tl3 & PTE_SW_WIRED) pmap->pm_stats.wired_count--; if ((tl3 & PTE_A) != 0) vm_page_aflag_set(m, PGA_REFERENCED); /* * Update the vm_page_t clean and reference bits. */ - if (pmap_page_dirty(tl3)) + if ((tl3 & PTE_D) != 0) vm_page_dirty(m); pmap_unuse_l3(pmap, pv->pv_va, pmap_load(l2), &free); TAILQ_REMOVE(&m->md.pv_list, pv, pv_next); m->md.pv_gen++; free_pv_entry(pmap, pv); PMAP_UNLOCK(pmap); } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&pvh_global_lock); vm_page_free_pages_toq(&free, false); } /* * Set the physical protection on the * specified range of this map as requested. */ void pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { vm_offset_t va_next; pd_entry_t *l1, *l2; pt_entry_t *l3p, l3; pt_entry_t entry; if ((prot & VM_PROT_READ) == VM_PROT_NONE) { pmap_remove(pmap, sva, eva); return; } if ((prot & VM_PROT_WRITE) == VM_PROT_WRITE) return; PMAP_LOCK(pmap); for (; sva < eva; sva = va_next) { l1 = pmap_l1(pmap, sva); if (pmap_load(l1) == 0) { va_next = (sva + L1_SIZE) & ~L1_OFFSET; if (va_next < sva) va_next = eva; continue; } va_next = (sva + L2_SIZE) & ~L2_OFFSET; if (va_next < sva) va_next = eva; l2 = pmap_l1_to_l2(l1, sva); if (l2 == NULL) continue; if (pmap_load(l2) == 0) continue; if ((pmap_load(l2) & PTE_RX) != 0) continue; if (va_next > eva) va_next = eva; for (l3p = pmap_l2_to_l3(l2, sva); sva != va_next; l3p++, sva += L3_SIZE) { l3 = pmap_load(l3p); - if (pmap_l3_valid(l3)) { + if ((l3 & PTE_V) != 0) { entry = pmap_load(l3p); - entry &= ~(PTE_W); + entry &= ~PTE_W; pmap_load_store(l3p, entry); /* XXX: Use pmap_invalidate_range */ pmap_invalidate_page(pmap, sva); } } } PMAP_UNLOCK(pmap); } int pmap_fault_fixup(pmap_t pmap, vm_offset_t va, vm_prot_t ftype) { pt_entry_t orig_l3; pt_entry_t new_l3; pt_entry_t *l3; int rv; rv = 0; PMAP_LOCK(pmap); l3 = pmap_l3(pmap, va); if (l3 == NULL) goto done; orig_l3 = pmap_load(l3); if ((orig_l3 & PTE_V) == 0 || (ftype == VM_PROT_WRITE && (orig_l3 & PTE_W) == 0) || (ftype == VM_PROT_EXECUTE && (orig_l3 & PTE_X) == 0) || (ftype == VM_PROT_READ && (orig_l3 & PTE_R) == 0)) goto done; new_l3 = orig_l3 | PTE_A; if (ftype == VM_PROT_WRITE) new_l3 |= PTE_D; if (orig_l3 != new_l3) { pmap_load_store(l3, new_l3); pmap_invalidate_page(pmap, va); rv = 1; goto done; } /* * XXX: This case should never happen since it means * the PTE shouldn't have resulted in a fault. */ done: PMAP_UNLOCK(pmap); return (rv); } /* * Insert the given physical page (p) at * the specified virtual address (v) in the * target physical map with the protection requested. * * If specified, the page will be wired down, meaning * that the related pte can not be reclaimed. * * NB: This is the only routine which MAY NOT lazy-evaluate * or lose information. That is, this routine must actually * insert this page into the given map NOW. */ int pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind __unused) { struct rwlock *lock; pd_entry_t *l1, *l2; pt_entry_t new_l3, orig_l3; pt_entry_t *l3; pv_entry_t pv; vm_paddr_t opa, pa, l2_pa, l3_pa; vm_page_t mpte, om, l2_m, l3_m; boolean_t nosleep; pt_entry_t entry; pn_t l2_pn; pn_t l3_pn; pn_t pn; va = trunc_page(va); if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); pa = VM_PAGE_TO_PHYS(m); pn = (pa / PAGE_SIZE); new_l3 = PTE_V | PTE_R | PTE_A; if (prot & VM_PROT_EXECUTE) new_l3 |= PTE_X; if (flags & VM_PROT_WRITE) new_l3 |= PTE_D; if (prot & VM_PROT_WRITE) new_l3 |= PTE_W; if ((va >> 63) == 0) new_l3 |= PTE_U; new_l3 |= (pn << PTE_PPN0_S); if ((flags & PMAP_ENTER_WIRED) != 0) new_l3 |= PTE_SW_WIRED; /* * Set modified bit gratuitously for writeable mappings if * the page is unmanaged. We do not want to take a fault * to do the dirty bit accounting for these mappings. */ if ((m->oflags & VPO_UNMANAGED) != 0) { if (prot & VM_PROT_WRITE) new_l3 |= PTE_D; } else new_l3 |= PTE_SW_MANAGED; CTR2(KTR_PMAP, "pmap_enter: %.16lx -> %.16lx", va, pa); mpte = NULL; lock = NULL; rw_rlock(&pvh_global_lock); PMAP_LOCK(pmap); if (va < VM_MAXUSER_ADDRESS) { nosleep = (flags & PMAP_ENTER_NOSLEEP) != 0; mpte = pmap_alloc_l3(pmap, va, nosleep ? NULL : &lock); if (mpte == NULL && nosleep) { CTR0(KTR_PMAP, "pmap_enter: mpte == NULL"); if (lock != NULL) rw_wunlock(lock); rw_runlock(&pvh_global_lock); PMAP_UNLOCK(pmap); return (KERN_RESOURCE_SHORTAGE); } l3 = pmap_l3(pmap, va); } else { l3 = pmap_l3(pmap, va); /* TODO: This is not optimal, but should mostly work */ if (l3 == NULL) { l2 = pmap_l2(pmap, va); if (l2 == NULL) { l2_m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO); if (l2_m == NULL) panic("pmap_enter: l2 pte_m == NULL"); if ((l2_m->flags & PG_ZERO) == 0) pmap_zero_page(l2_m); l2_pa = VM_PAGE_TO_PHYS(l2_m); l2_pn = (l2_pa / PAGE_SIZE); l1 = pmap_l1(pmap, va); entry = (PTE_V); entry |= (l2_pn << PTE_PPN0_S); pmap_load_store(l1, entry); pmap_distribute_l1(pmap, pmap_l1_index(va), entry); l2 = pmap_l1_to_l2(l1, va); } KASSERT(l2 != NULL, ("No l2 table after allocating one")); l3_m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO); if (l3_m == NULL) panic("pmap_enter: l3 pte_m == NULL"); if ((l3_m->flags & PG_ZERO) == 0) pmap_zero_page(l3_m); l3_pa = VM_PAGE_TO_PHYS(l3_m); l3_pn = (l3_pa / PAGE_SIZE); entry = (PTE_V); entry |= (l3_pn << PTE_PPN0_S); pmap_load_store(l2, entry); l3 = pmap_l2_to_l3(l2, va); } pmap_invalidate_page(pmap, va); } orig_l3 = pmap_load(l3); opa = PTE_TO_PHYS(orig_l3); pv = NULL; /* * Is the specified virtual address already mapped? */ - if (pmap_l3_valid(orig_l3)) { + if ((orig_l3 & PTE_V) != 0) { /* * Wiring change, just update stats. We don't worry about * wiring PT pages as they remain resident as long as there * are valid mappings in them. Hence, if a user page is wired, * the PT page will be also. */ if ((flags & PMAP_ENTER_WIRED) != 0 && (orig_l3 & PTE_SW_WIRED) == 0) pmap->pm_stats.wired_count++; else if ((flags & PMAP_ENTER_WIRED) == 0 && (orig_l3 & PTE_SW_WIRED) != 0) pmap->pm_stats.wired_count--; /* * Remove the extra PT page reference. */ if (mpte != NULL) { mpte->wire_count--; KASSERT(mpte->wire_count > 0, ("pmap_enter: missing reference to page table page," " va: 0x%lx", va)); } /* * Has the physical page changed? */ if (opa == pa) { /* * No, might be a protection or wiring change. */ - if ((orig_l3 & PTE_SW_MANAGED) != 0) { - if (pmap_is_write(new_l3)) - vm_page_aflag_set(m, PGA_WRITEABLE); - } + if ((orig_l3 & PTE_SW_MANAGED) != 0 && + (new_l3 & PTE_W) != 0) + vm_page_aflag_set(m, PGA_WRITEABLE); goto validate; } /* * The physical page has changed. Temporarily invalidate * the mapping. This ensures that all threads sharing the * pmap keep a consistent view of the mapping, which is * necessary for the correct handling of COW faults. It * also permits reuse of the old mapping's PV entry, * avoiding an allocation. * * For consistency, handle unmanaged mappings the same way. */ orig_l3 = pmap_load_clear(l3); KASSERT(PTE_TO_PHYS(orig_l3) == opa, ("pmap_enter: unexpected pa update for %#lx", va)); if ((orig_l3 & PTE_SW_MANAGED) != 0) { om = PHYS_TO_VM_PAGE(opa); /* * The pmap lock is sufficient to synchronize with * concurrent calls to pmap_page_test_mappings() and * pmap_ts_referenced(). */ - if (pmap_page_dirty(orig_l3)) + if ((orig_l3 & PTE_D) != 0) vm_page_dirty(om); if ((orig_l3 & PTE_A) != 0) vm_page_aflag_set(om, PGA_REFERENCED); CHANGE_PV_LIST_LOCK_TO_PHYS(&lock, opa); pv = pmap_pvh_remove(&om->md, pmap, va); if ((new_l3 & PTE_SW_MANAGED) == 0) free_pv_entry(pmap, pv); if ((om->aflags & PGA_WRITEABLE) != 0 && TAILQ_EMPTY(&om->md.pv_list)) vm_page_aflag_clear(om, PGA_WRITEABLE); } pmap_invalidate_page(pmap, va); orig_l3 = 0; } else { /* * Increment the counters. */ if ((new_l3 & PTE_SW_WIRED) != 0) pmap->pm_stats.wired_count++; pmap_resident_count_inc(pmap, 1); } /* * Enter on the PV list if part of our managed memory. */ if ((new_l3 & PTE_SW_MANAGED) != 0) { if (pv == NULL) { pv = get_pv_entry(pmap, &lock); pv->pv_va = va; } CHANGE_PV_LIST_LOCK_TO_PHYS(&lock, pa); TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next); m->md.pv_gen++; - if (pmap_is_write(new_l3)) + if ((new_l3 & PTE_W) != 0) vm_page_aflag_set(m, PGA_WRITEABLE); } validate: /* * Sync the i-cache on all harts before updating the PTE * if the new PTE is executable. */ if (prot & VM_PROT_EXECUTE) pmap_sync_icache(pmap, va, PAGE_SIZE); /* * Update the L3 entry. */ if (orig_l3 != 0) { orig_l3 = pmap_load_store(l3, new_l3); pmap_invalidate_page(pmap, va); KASSERT(PTE_TO_PHYS(orig_l3) == pa, ("pmap_enter: invalid update")); - if (pmap_page_dirty(orig_l3) && - (orig_l3 & PTE_SW_MANAGED) != 0) + if ((orig_l3 & (PTE_D | PTE_SW_MANAGED)) == + (PTE_D | PTE_SW_MANAGED)) vm_page_dirty(m); } else { pmap_load_store(l3, new_l3); } if (lock != NULL) rw_wunlock(lock); rw_runlock(&pvh_global_lock); PMAP_UNLOCK(pmap); return (KERN_SUCCESS); } /* * Maps a sequence of resident pages belonging to the same object. * The sequence begins with the given page m_start. This page is * mapped at the given virtual address start. Each subsequent page is * mapped at a virtual address that is offset from start by the same * amount as the page is offset from m_start within the object. The * last page in the sequence is the page with the largest offset from * m_start that can be mapped at a virtual address less than the given * virtual address end. Not every virtual page between start and end * is mapped; only those for which a resident page exists with the * corresponding offset from m_start are mapped. */ void pmap_enter_object(pmap_t pmap, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { struct rwlock *lock; vm_offset_t va; vm_page_t m, mpte; vm_pindex_t diff, psize; VM_OBJECT_ASSERT_LOCKED(m_start->object); psize = atop(end - start); mpte = NULL; m = m_start; lock = NULL; rw_rlock(&pvh_global_lock); PMAP_LOCK(pmap); while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) { va = start + ptoa(diff); mpte = pmap_enter_quick_locked(pmap, va, m, prot, mpte, &lock); m = TAILQ_NEXT(m, listq); } if (lock != NULL) rw_wunlock(lock); rw_runlock(&pvh_global_lock); PMAP_UNLOCK(pmap); } /* * this code makes some *MAJOR* assumptions: * 1. Current pmap & pmap exists. * 2. Not wired. * 3. Read access. * 4. No page table pages. * but is *MUCH* faster than pmap_enter... */ void pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot) { struct rwlock *lock; lock = NULL; rw_rlock(&pvh_global_lock); PMAP_LOCK(pmap); (void)pmap_enter_quick_locked(pmap, va, m, prot, NULL, &lock); if (lock != NULL) rw_wunlock(lock); rw_runlock(&pvh_global_lock); PMAP_UNLOCK(pmap); } static vm_page_t pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, vm_page_t mpte, struct rwlock **lockp) { struct spglist free; vm_paddr_t phys; pd_entry_t *l2; pt_entry_t *l3; vm_paddr_t pa; pt_entry_t entry; pn_t pn; KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva || (m->oflags & VPO_UNMANAGED) != 0, ("pmap_enter_quick_locked: managed mapping within the clean submap")); rw_assert(&pvh_global_lock, RA_LOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); CTR2(KTR_PMAP, "pmap_enter_quick_locked: %p %lx", pmap, va); /* * In the case that a page table page is not * resident, we are creating it here. */ if (va < VM_MAXUSER_ADDRESS) { vm_pindex_t l2pindex; /* * Calculate pagetable page index */ l2pindex = pmap_l2_pindex(va); if (mpte && (mpte->pindex == l2pindex)) { mpte->wire_count++; } else { /* * Get the l2 entry */ l2 = pmap_l2(pmap, va); /* * If the page table page is mapped, we just increment * the hold count, and activate it. Otherwise, we * attempt to allocate a page table page. If this * attempt fails, we don't retry. Instead, we give up. */ if (l2 != NULL && pmap_load(l2) != 0) { phys = PTE_TO_PHYS(pmap_load(l2)); mpte = PHYS_TO_VM_PAGE(phys); mpte->wire_count++; } else { /* * Pass NULL instead of the PV list lock * pointer, because we don't intend to sleep. */ mpte = _pmap_alloc_l3(pmap, l2pindex, NULL); if (mpte == NULL) return (mpte); } } l3 = (pt_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(mpte)); l3 = &l3[pmap_l3_index(va)]; } else { mpte = NULL; l3 = pmap_l3(kernel_pmap, va); } if (l3 == NULL) panic("pmap_enter_quick_locked: No l3"); if (pmap_load(l3) != 0) { if (mpte != NULL) { mpte->wire_count--; mpte = NULL; } return (mpte); } /* * Enter on the PV list if part of our managed memory. */ if ((m->oflags & VPO_UNMANAGED) == 0 && !pmap_try_insert_pv_entry(pmap, va, m, lockp)) { if (mpte != NULL) { SLIST_INIT(&free); if (pmap_unwire_l3(pmap, va, mpte, &free)) { pmap_invalidate_page(pmap, va); vm_page_free_pages_toq(&free, false); } mpte = NULL; } return (mpte); } /* * Increment counters */ pmap_resident_count_inc(pmap, 1); pa = VM_PAGE_TO_PHYS(m); pn = (pa / PAGE_SIZE); entry = PTE_V | PTE_R; if (prot & VM_PROT_EXECUTE) entry |= PTE_X; entry |= (pn << PTE_PPN0_S); /* * Now validate mapping with RO protection */ if ((m->oflags & VPO_UNMANAGED) == 0) entry |= PTE_SW_MANAGED; /* * Sync the i-cache on all harts before updating the PTE * if the new PTE is executable. */ if (prot & VM_PROT_EXECUTE) pmap_sync_icache(pmap, va, PAGE_SIZE); pmap_load_store(l3, entry); pmap_invalidate_page(pmap, va); return (mpte); } /* * This code maps large physical mmap regions into the * processor address space. Note that some shortcuts * are taken, but the code works. */ void pmap_object_init_pt(pmap_t pmap, vm_offset_t addr, vm_object_t object, vm_pindex_t pindex, vm_size_t size) { VM_OBJECT_ASSERT_WLOCKED(object); KASSERT(object->type == OBJT_DEVICE || object->type == OBJT_SG, ("pmap_object_init_pt: non-device object")); } /* * Clear the wired attribute from the mappings for the specified range of * addresses in the given pmap. Every valid mapping within that range * must have the wired attribute set. In contrast, invalid mappings * cannot have the wired attribute set, so they are ignored. * * The wired attribute of the page table entry is not a hardware feature, * so there is no need to invalidate any TLB entries. */ void pmap_unwire(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { vm_offset_t va_next; pd_entry_t *l1, *l2; pt_entry_t *l3; boolean_t pv_lists_locked; pv_lists_locked = FALSE; PMAP_LOCK(pmap); for (; sva < eva; sva = va_next) { l1 = pmap_l1(pmap, sva); if (pmap_load(l1) == 0) { va_next = (sva + L1_SIZE) & ~L1_OFFSET; if (va_next < sva) va_next = eva; continue; } va_next = (sva + L2_SIZE) & ~L2_OFFSET; if (va_next < sva) va_next = eva; l2 = pmap_l1_to_l2(l1, sva); if (pmap_load(l2) == 0) continue; if (va_next > eva) va_next = eva; for (l3 = pmap_l2_to_l3(l2, sva); sva != va_next; l3++, sva += L3_SIZE) { if (pmap_load(l3) == 0) continue; if ((pmap_load(l3) & PTE_SW_WIRED) == 0) panic("pmap_unwire: l3 %#jx is missing " "PTE_SW_WIRED", (uintmax_t)*l3); /* * PG_W must be cleared atomically. Although the pmap * lock synchronizes access to PG_W, another processor * could be setting PG_M and/or PG_A concurrently. */ atomic_clear_long(l3, PTE_SW_WIRED); pmap->pm_stats.wired_count--; } } if (pv_lists_locked) rw_runlock(&pvh_global_lock); PMAP_UNLOCK(pmap); } /* * Copy the range specified by src_addr/len * from the source map to the range dst_addr/len * in the destination map. * * This routine is only advisory and need not do anything. */ void pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len, vm_offset_t src_addr) { } /* * pmap_zero_page zeros the specified hardware page by mapping * the page into KVM and using bzero to clear its contents. */ void pmap_zero_page(vm_page_t m) { vm_offset_t va = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m)); pagezero((void *)va); } /* * pmap_zero_page_area zeros the specified hardware page by mapping * the page into KVM and using bzero to clear its contents. * * off and size may not cover an area beyond a single hardware page. */ void pmap_zero_page_area(vm_page_t m, int off, int size) { vm_offset_t va = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m)); if (off == 0 && size == PAGE_SIZE) pagezero((void *)va); else bzero((char *)va + off, size); } /* * pmap_copy_page copies the specified (machine independent) * page by mapping the page into virtual memory and using * bcopy to copy the page, one machine dependent page at a * time. */ void pmap_copy_page(vm_page_t msrc, vm_page_t mdst) { vm_offset_t src = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(msrc)); vm_offset_t dst = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(mdst)); pagecopy((void *)src, (void *)dst); } int unmapped_buf_allowed = 1; void pmap_copy_pages(vm_page_t ma[], vm_offset_t a_offset, vm_page_t mb[], vm_offset_t b_offset, int xfersize) { void *a_cp, *b_cp; vm_page_t m_a, m_b; vm_paddr_t p_a, p_b; vm_offset_t a_pg_offset, b_pg_offset; int cnt; while (xfersize > 0) { a_pg_offset = a_offset & PAGE_MASK; m_a = ma[a_offset >> PAGE_SHIFT]; p_a = m_a->phys_addr; b_pg_offset = b_offset & PAGE_MASK; m_b = mb[b_offset >> PAGE_SHIFT]; p_b = m_b->phys_addr; cnt = min(xfersize, PAGE_SIZE - a_pg_offset); cnt = min(cnt, PAGE_SIZE - b_pg_offset); if (__predict_false(!PHYS_IN_DMAP(p_a))) { panic("!DMAP a %lx", p_a); } else { a_cp = (char *)PHYS_TO_DMAP(p_a) + a_pg_offset; } if (__predict_false(!PHYS_IN_DMAP(p_b))) { panic("!DMAP b %lx", p_b); } else { b_cp = (char *)PHYS_TO_DMAP(p_b) + b_pg_offset; } bcopy(a_cp, b_cp, cnt); a_offset += cnt; b_offset += cnt; xfersize -= cnt; } } vm_offset_t pmap_quick_enter_page(vm_page_t m) { return (PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m))); } void pmap_quick_remove_page(vm_offset_t addr) { } /* * Returns true if the pmap's pv is one of the first * 16 pvs linked to from this page. This count may * be changed upwards or downwards in the future; it * is only necessary that true be returned for a small * subset of pmaps for proper page aging. */ boolean_t pmap_page_exists_quick(pmap_t pmap, vm_page_t m) { struct rwlock *lock; pv_entry_t pv; int loops = 0; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_page_exists_quick: page %p is not managed", m)); rv = FALSE; rw_rlock(&pvh_global_lock); lock = VM_PAGE_TO_PV_LIST_LOCK(m); rw_rlock(lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_next) { if (PV_PMAP(pv) == pmap) { rv = TRUE; break; } loops++; if (loops >= 16) break; } rw_runlock(lock); rw_runlock(&pvh_global_lock); return (rv); } /* * pmap_page_wired_mappings: * * Return the number of managed mappings to the given physical page * that are wired. */ int pmap_page_wired_mappings(vm_page_t m) { struct rwlock *lock; pmap_t pmap; pt_entry_t *l3; pv_entry_t pv; int count, md_gen; if ((m->oflags & VPO_UNMANAGED) != 0) return (0); rw_rlock(&pvh_global_lock); lock = VM_PAGE_TO_PV_LIST_LOCK(m); rw_rlock(lock); restart: count = 0; TAILQ_FOREACH(pv, &m->md.pv_list, pv_next) { pmap = PV_PMAP(pv); if (!PMAP_TRYLOCK(pmap)) { md_gen = m->md.pv_gen; rw_runlock(lock); PMAP_LOCK(pmap); rw_rlock(lock); if (md_gen != m->md.pv_gen) { PMAP_UNLOCK(pmap); goto restart; } } l3 = pmap_l3(pmap, pv->pv_va); if (l3 != NULL && (pmap_load(l3) & PTE_SW_WIRED) != 0) count++; PMAP_UNLOCK(pmap); } rw_runlock(lock); rw_runlock(&pvh_global_lock); return (count); } /* * Destroy all managed, non-wired mappings in the given user-space * pmap. This pmap cannot be active on any processor besides the * caller. * * This function cannot be applied to the kernel pmap. Moreover, it * is not intended for general use. It is only to be used during * process termination. Consequently, it can be implemented in ways * that make it faster than pmap_remove(). First, it can more quickly * destroy mappings by iterating over the pmap's collection of PV * entries, rather than searching the page table. Second, it doesn't * have to test and clear the page table entries atomically, because * no processor is currently accessing the user address space. In * particular, a page table entry's dirty bit won't change state once * this function starts. */ void pmap_remove_pages(pmap_t pmap) { pd_entry_t ptepde, *l2; pt_entry_t *l3, tl3; struct spglist free; vm_page_t m; pv_entry_t pv; struct pv_chunk *pc, *npc; struct rwlock *lock; int64_t bit; uint64_t inuse, bitmask; int allfree, field, freed, idx; vm_paddr_t pa; lock = NULL; SLIST_INIT(&free); rw_rlock(&pvh_global_lock); PMAP_LOCK(pmap); TAILQ_FOREACH_SAFE(pc, &pmap->pm_pvchunk, pc_list, npc) { allfree = 1; freed = 0; for (field = 0; field < _NPCM; field++) { inuse = ~pc->pc_map[field] & pc_freemask[field]; while (inuse != 0) { bit = ffsl(inuse) - 1; bitmask = 1UL << bit; idx = field * 64 + bit; pv = &pc->pc_pventry[idx]; inuse &= ~bitmask; l2 = pmap_l2(pmap, pv->pv_va); ptepde = pmap_load(l2); l3 = pmap_l2_to_l3(l2, pv->pv_va); tl3 = pmap_load(l3); /* * We cannot remove wired pages from a process' mapping at this time */ if (tl3 & PTE_SW_WIRED) { allfree = 0; continue; } pa = PTE_TO_PHYS(tl3); m = PHYS_TO_VM_PAGE(pa); KASSERT(m->phys_addr == pa, ("vm_page_t %p phys_addr mismatch %016jx %016jx", m, (uintmax_t)m->phys_addr, (uintmax_t)tl3)); KASSERT((m->flags & PG_FICTITIOUS) != 0 || m < &vm_page_array[vm_page_array_size], ("pmap_remove_pages: bad l3 %#jx", (uintmax_t)tl3)); pmap_load_clear(l3); pmap_invalidate_page(pmap, pv->pv_va); /* * Update the vm_page_t clean/reference bits. */ - if (pmap_page_dirty(tl3)) + if ((tl3 & PTE_D) != 0) vm_page_dirty(m); CHANGE_PV_LIST_LOCK_TO_VM_PAGE(&lock, m); /* Mark free */ pc->pc_map[field] |= bitmask; pmap_resident_count_dec(pmap, 1); TAILQ_REMOVE(&m->md.pv_list, pv, pv_next); m->md.pv_gen++; pmap_unuse_l3(pmap, pv->pv_va, ptepde, &free); freed++; } } PV_STAT(atomic_add_long(&pv_entry_frees, freed)); PV_STAT(atomic_add_int(&pv_entry_spare, freed)); PV_STAT(atomic_subtract_long(&pv_entry_count, freed)); if (allfree) { TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); free_pv_chunk(pc); } } pmap_invalidate_all(pmap); if (lock != NULL) rw_wunlock(lock); rw_runlock(&pvh_global_lock); PMAP_UNLOCK(pmap); vm_page_free_pages_toq(&free, false); } /* * This is used to check if a page has been accessed or modified. As we * don't have a bit to see if it has been modified we have to assume it * has been if the page is read/write. */ static boolean_t pmap_page_test_mappings(vm_page_t m, boolean_t accessed, boolean_t modified) { struct rwlock *lock; pv_entry_t pv; pt_entry_t *l3, mask, value; pmap_t pmap; int md_gen; boolean_t rv; rv = FALSE; rw_rlock(&pvh_global_lock); lock = VM_PAGE_TO_PV_LIST_LOCK(m); rw_rlock(lock); restart: TAILQ_FOREACH(pv, &m->md.pv_list, pv_next) { pmap = PV_PMAP(pv); if (!PMAP_TRYLOCK(pmap)) { md_gen = m->md.pv_gen; rw_runlock(lock); PMAP_LOCK(pmap); rw_rlock(lock); if (md_gen != m->md.pv_gen) { PMAP_UNLOCK(pmap); goto restart; } } l3 = pmap_l3(pmap, pv->pv_va); mask = 0; value = 0; if (modified) { mask |= PTE_D; value |= PTE_D; } if (accessed) { mask |= PTE_A; value |= PTE_A; } #if 0 if (modified) { mask |= ATTR_AP_RW_BIT; value |= ATTR_AP(ATTR_AP_RW); } if (accessed) { mask |= ATTR_AF | ATTR_DESCR_MASK; value |= ATTR_AF | L3_PAGE; } #endif rv = (pmap_load(l3) & mask) == value; PMAP_UNLOCK(pmap); if (rv) goto out; } out: rw_runlock(lock); rw_runlock(&pvh_global_lock); return (rv); } /* * pmap_is_modified: * * Return whether or not the specified physical page was modified * in any physical maps. */ boolean_t pmap_is_modified(vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_is_modified: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have PG_M set. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (FALSE); return (pmap_page_test_mappings(m, FALSE, TRUE)); } /* * pmap_is_prefaultable: * * Return whether or not the specified virtual address is eligible * for prefault. */ boolean_t pmap_is_prefaultable(pmap_t pmap, vm_offset_t addr) { pt_entry_t *l3; boolean_t rv; rv = FALSE; PMAP_LOCK(pmap); l3 = pmap_l3(pmap, addr); if (l3 != NULL && pmap_load(l3) != 0) { rv = TRUE; } PMAP_UNLOCK(pmap); return (rv); } /* * pmap_is_referenced: * * Return whether or not the specified physical page was referenced * in any physical maps. */ boolean_t pmap_is_referenced(vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_is_referenced: page %p is not managed", m)); return (pmap_page_test_mappings(m, TRUE, FALSE)); } /* * Clear the write and modified bits in each of the given page's mappings. */ void pmap_remove_write(vm_page_t m) { pmap_t pmap; struct rwlock *lock; pv_entry_t pv; pt_entry_t *l3, oldl3; pt_entry_t newl3; int md_gen; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_write: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * set by another thread while the object is locked. Thus, * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_rlock(&pvh_global_lock); lock = VM_PAGE_TO_PV_LIST_LOCK(m); retry_pv_loop: rw_wlock(lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_next) { pmap = PV_PMAP(pv); if (!PMAP_TRYLOCK(pmap)) { md_gen = m->md.pv_gen; rw_wunlock(lock); PMAP_LOCK(pmap); rw_wlock(lock); if (md_gen != m->md.pv_gen) { PMAP_UNLOCK(pmap); rw_wunlock(lock); goto retry_pv_loop; } } l3 = pmap_l3(pmap, pv->pv_va); retry: oldl3 = pmap_load(l3); - if (pmap_is_write(oldl3)) { - newl3 = oldl3 & ~(PTE_W); + if ((oldl3 & PTE_W) != 0) { + newl3 = oldl3 & ~PTE_W; if (!atomic_cmpset_long(l3, oldl3, newl3)) goto retry; - /* TODO: use pmap_page_dirty(oldl3) ? */ + /* TODO: check for PTE_D? */ if ((oldl3 & PTE_A) != 0) vm_page_dirty(m); pmap_invalidate_page(pmap, pv->pv_va); } PMAP_UNLOCK(pmap); } rw_wunlock(lock); vm_page_aflag_clear(m, PGA_WRITEABLE); rw_runlock(&pvh_global_lock); } static __inline boolean_t safe_to_clear_referenced(pmap_t pmap, pt_entry_t pte) { return (FALSE); } /* * pmap_ts_referenced: * * Return a count of reference bits for a page, clearing those bits. * It is not necessary for every reference bit to be cleared, but it * is necessary that 0 only be returned when there are truly no * reference bits set. * * As an optimization, update the page's dirty field if a modified bit is * found while counting reference bits. This opportunistic update can be * performed at low cost and can eliminate the need for some future calls * to pmap_is_modified(). However, since this function stops after * finding PMAP_TS_REFERENCED_MAX reference bits, it may not detect some * dirty pages. Those dirty pages will only be detected by a future call * to pmap_is_modified(). */ int pmap_ts_referenced(vm_page_t m) { pv_entry_t pv, pvf; pmap_t pmap; struct rwlock *lock; pd_entry_t *l2; pt_entry_t *l3, old_l3; vm_paddr_t pa; int cleared, md_gen, not_cleared; struct spglist free; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_ts_referenced: page %p is not managed", m)); SLIST_INIT(&free); cleared = 0; pa = VM_PAGE_TO_PHYS(m); lock = PHYS_TO_PV_LIST_LOCK(pa); rw_rlock(&pvh_global_lock); rw_wlock(lock); retry: not_cleared = 0; if ((pvf = TAILQ_FIRST(&m->md.pv_list)) == NULL) goto out; pv = pvf; do { if (pvf == NULL) pvf = pv; pmap = PV_PMAP(pv); if (!PMAP_TRYLOCK(pmap)) { md_gen = m->md.pv_gen; rw_wunlock(lock); PMAP_LOCK(pmap); rw_wlock(lock); if (md_gen != m->md.pv_gen) { PMAP_UNLOCK(pmap); goto retry; } } l2 = pmap_l2(pmap, pv->pv_va); KASSERT((pmap_load(l2) & PTE_RX) == 0, ("pmap_ts_referenced: found an invalid l2 table")); l3 = pmap_l2_to_l3(l2, pv->pv_va); old_l3 = pmap_load(l3); - if (pmap_page_dirty(old_l3)) + if ((old_l3 & PTE_D) != 0) vm_page_dirty(m); if ((old_l3 & PTE_A) != 0) { if (safe_to_clear_referenced(pmap, old_l3)) { /* * TODO: We don't handle the access flag * at all. We need to be able to set it in * the exception handler. */ panic("RISCVTODO: safe_to_clear_referenced\n"); } else if ((old_l3 & PTE_SW_WIRED) == 0) { /* * Wired pages cannot be paged out so * doing accessed bit emulation for * them is wasted effort. We do the * hard work for unwired pages only. */ pmap_remove_l3(pmap, l3, pv->pv_va, pmap_load(l2), &free, &lock); pmap_invalidate_page(pmap, pv->pv_va); cleared++; if (pvf == pv) pvf = NULL; pv = NULL; KASSERT(lock == VM_PAGE_TO_PV_LIST_LOCK(m), ("inconsistent pv lock %p %p for page %p", lock, VM_PAGE_TO_PV_LIST_LOCK(m), m)); } else not_cleared++; } PMAP_UNLOCK(pmap); /* Rotate the PV list if it has more than one entry. */ if (pv != NULL && TAILQ_NEXT(pv, pv_next) != NULL) { TAILQ_REMOVE(&m->md.pv_list, pv, pv_next); TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next); m->md.pv_gen++; } } while ((pv = TAILQ_FIRST(&m->md.pv_list)) != pvf && cleared + not_cleared < PMAP_TS_REFERENCED_MAX); out: rw_wunlock(lock); rw_runlock(&pvh_global_lock); vm_page_free_pages_toq(&free, false); return (cleared + not_cleared); } /* * Apply the given advice to the specified range of addresses within the * given pmap. Depending on the advice, clear the referenced and/or * modified flags in each mapping and set the mapped page's dirty field. */ void pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice) { } /* * Clear the modify bits on the specified physical page. */ void pmap_clear_modify(vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); KASSERT(!vm_page_xbusied(m), ("pmap_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have PG_M set. * If the object containing the page is locked and the page is not * exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; /* RISCVTODO: We lack support for tracking if a page is modified */ } void * pmap_mapbios(vm_paddr_t pa, vm_size_t size) { return ((void *)PHYS_TO_DMAP(pa)); } void pmap_unmapbios(vm_paddr_t pa, vm_size_t size) { } /* * Sets the memory attribute for the specified page. */ void pmap_page_set_memattr(vm_page_t m, vm_memattr_t ma) { m->md.pv_memattr = ma; /* * RISCVTODO: Implement the below (from the amd64 pmap) * If "m" is a normal page, update its direct mapping. This update * can be relied upon to perform any cache operations that are * required for data coherence. */ if ((m->flags & PG_FICTITIOUS) == 0 && PHYS_IN_DMAP(VM_PAGE_TO_PHYS(m))) panic("RISCVTODO: pmap_page_set_memattr"); } /* * perform the pmap work for mincore */ int pmap_mincore(pmap_t pmap, vm_offset_t addr, vm_paddr_t *locked_pa) { pt_entry_t *l2, *l3, tpte; vm_paddr_t pa; int val; bool managed; PMAP_LOCK(pmap); retry: managed = false; val = 0; l2 = pmap_l2(pmap, addr); if (l2 != NULL && ((tpte = pmap_load(l2)) & PTE_V) != 0) { if ((tpte & (PTE_R | PTE_W | PTE_X)) != 0) { pa = PTE_TO_PHYS(tpte) | (addr & L2_OFFSET); val = MINCORE_INCORE | MINCORE_SUPER; } else { l3 = pmap_l2_to_l3(l2, addr); tpte = pmap_load(l3); if ((tpte & PTE_V) == 0) goto done; pa = PTE_TO_PHYS(tpte) | (addr & L3_OFFSET); val = MINCORE_INCORE; } - if (pmap_page_dirty(tpte)) + if ((tpte & PTE_D) != 0) val |= MINCORE_MODIFIED | MINCORE_MODIFIED_OTHER; - if (pmap_page_accessed(tpte)) + if ((tpte & PTE_A) != 0) val |= MINCORE_REFERENCED | MINCORE_REFERENCED_OTHER; managed = (tpte & PTE_SW_MANAGED) == PTE_SW_MANAGED; } done: if ((val & (MINCORE_MODIFIED_OTHER | MINCORE_REFERENCED_OTHER)) != (MINCORE_MODIFIED_OTHER | MINCORE_REFERENCED_OTHER) && managed) { /* Ensure that "PHYS_TO_VM_PAGE(pa)->object" doesn't change. */ if (vm_page_pa_tryrelock(pmap, pa, locked_pa)) goto retry; } else PA_UNLOCK_COND(*locked_pa); PMAP_UNLOCK(pmap); return (val); } void pmap_activate(struct thread *td) { pmap_t pmap; uint64_t reg; critical_enter(); pmap = vmspace_pmap(td->td_proc->p_vmspace); td->td_pcb->pcb_l1addr = vtophys(pmap->pm_l1); reg = SATP_MODE_SV39; reg |= (td->td_pcb->pcb_l1addr >> PAGE_SHIFT); load_satp(reg); pmap_invalidate_all(pmap); critical_exit(); } void pmap_sync_icache(pmap_t pm, vm_offset_t va, vm_size_t sz) { cpuset_t mask; /* * From the RISC-V User-Level ISA V2.2: * * "To make a store to instruction memory visible to all * RISC-V harts, the writing hart has to execute a data FENCE * before requesting that all remote RISC-V harts execute a * FENCE.I." */ sched_pin(); mask = all_cpus; CPU_CLR(PCPU_GET(cpuid), &mask); fence(); sbi_remote_fence_i(mask.__bits); sched_unpin(); } /* * Increase the starting virtual address of the given mapping if a * different alignment might result in more superpage mappings. */ void pmap_align_superpage(vm_object_t object, vm_ooffset_t offset, vm_offset_t *addr, vm_size_t size) { } /** * Get the kernel virtual address of a set of physical pages. If there are * physical addresses not covered by the DMAP perform a transient mapping * that will be removed when calling pmap_unmap_io_transient. * * \param page The pages the caller wishes to obtain the virtual * address on the kernel memory map. * \param vaddr On return contains the kernel virtual memory address * of the pages passed in the page parameter. * \param count Number of pages passed in. * \param can_fault TRUE if the thread using the mapped pages can take * page faults, FALSE otherwise. * * \returns TRUE if the caller must call pmap_unmap_io_transient when * finished or FALSE otherwise. * */ boolean_t pmap_map_io_transient(vm_page_t page[], vm_offset_t vaddr[], int count, boolean_t can_fault) { vm_paddr_t paddr; boolean_t needs_mapping; int error, i; /* * Allocate any KVA space that we need, this is done in a separate * loop to prevent calling vmem_alloc while pinned. */ needs_mapping = FALSE; for (i = 0; i < count; i++) { paddr = VM_PAGE_TO_PHYS(page[i]); if (__predict_false(paddr >= DMAP_MAX_PHYSADDR)) { error = vmem_alloc(kernel_arena, PAGE_SIZE, M_BESTFIT | M_WAITOK, &vaddr[i]); KASSERT(error == 0, ("vmem_alloc failed: %d", error)); needs_mapping = TRUE; } else { vaddr[i] = PHYS_TO_DMAP(paddr); } } /* Exit early if everything is covered by the DMAP */ if (!needs_mapping) return (FALSE); if (!can_fault) sched_pin(); for (i = 0; i < count; i++) { paddr = VM_PAGE_TO_PHYS(page[i]); if (paddr >= DMAP_MAX_PHYSADDR) { panic( "pmap_map_io_transient: TODO: Map out of DMAP data"); } } return (needs_mapping); } void pmap_unmap_io_transient(vm_page_t page[], vm_offset_t vaddr[], int count, boolean_t can_fault) { vm_paddr_t paddr; int i; if (!can_fault) sched_unpin(); for (i = 0; i < count; i++) { paddr = VM_PAGE_TO_PHYS(page[i]); if (paddr >= DMAP_MAX_PHYSADDR) { panic("RISCVTODO: pmap_unmap_io_transient: Unmap data"); } } } boolean_t pmap_is_valid_memattr(pmap_t pmap __unused, vm_memattr_t mode) { return (mode >= VM_MEMATTR_DEVICE && mode <= VM_MEMATTR_WRITE_BACK); } Index: projects/clang700-import/sys/x86/x86/mp_x86.c =================================================================== --- projects/clang700-import/sys/x86/x86/mp_x86.c (revision 341812) +++ projects/clang700-import/sys/x86/x86/mp_x86.c (revision 341813) @@ -1,1767 +1,1781 @@ /*- * Copyright (c) 1996, by Steve Passe * Copyright (c) 2003, by Peter Wemm * 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. The name of the developer 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$"); #ifdef __i386__ #include "opt_apic.h" #endif #include "opt_cpu.h" #include "opt_kstack_pages.h" #include "opt_pmap.h" #include "opt_sched.h" #include "opt_smp.h" #include #include #include #include /* cngetc() */ #include #ifdef GPROF #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_CPUS, "cpus", "CPU items"); /* lock region used by kernel profiling */ int mcount_lock; int mp_naps; /* # of Applications processors */ int boot_cpu_id = -1; /* designated BSP */ /* AP uses this during bootstrap. Do not staticize. */ char *bootSTK; int bootAP; /* Free these after use */ void *bootstacks[MAXCPU]; void *dpcpu; struct pcb stoppcbs[MAXCPU]; struct susppcb **susppcbs; #ifdef COUNT_IPIS /* Interrupt counts. */ static u_long *ipi_preempt_counts[MAXCPU]; static u_long *ipi_ast_counts[MAXCPU]; u_long *ipi_invltlb_counts[MAXCPU]; u_long *ipi_invlrng_counts[MAXCPU]; u_long *ipi_invlpg_counts[MAXCPU]; u_long *ipi_invlcache_counts[MAXCPU]; u_long *ipi_rendezvous_counts[MAXCPU]; static u_long *ipi_hardclock_counts[MAXCPU]; #endif /* Default cpu_ops implementation. */ struct cpu_ops cpu_ops; /* * Local data and functions. */ static volatile cpuset_t ipi_stop_nmi_pending; volatile cpuset_t resuming_cpus; volatile cpuset_t toresume_cpus; /* used to hold the AP's until we are ready to release them */ struct mtx ap_boot_mtx; /* Set to 1 once we're ready to let the APs out of the pen. */ volatile int aps_ready = 0; /* * Store data from cpu_add() until later in the boot when we actually setup * the APs. */ struct cpu_info *cpu_info; int *apic_cpuids; int cpu_apic_ids[MAXCPU]; _Static_assert(MAXCPU <= MAX_APIC_ID, "MAXCPU cannot be larger that MAX_APIC_ID"); _Static_assert(xAPIC_MAX_APIC_ID <= MAX_APIC_ID, "xAPIC_MAX_APIC_ID cannot be larger that MAX_APIC_ID"); /* Holds pending bitmap based IPIs per CPU */ volatile u_int cpu_ipi_pending[MAXCPU]; static void release_aps(void *dummy); static void cpustop_handler_post(u_int cpu); static int hyperthreading_allowed = 1; SYSCTL_INT(_machdep, OID_AUTO, hyperthreading_allowed, CTLFLAG_RDTUN, &hyperthreading_allowed, 0, "Use Intel HTT logical CPUs"); static struct topo_node topo_root; static int pkg_id_shift; static int node_id_shift; static int core_id_shift; static int disabled_cpus; struct cache_info { int id_shift; int present; } static caches[MAX_CACHE_LEVELS]; unsigned int boot_address; #define MiB(v) (v ## ULL << 20) void mem_range_AP_init(void) { if (mem_range_softc.mr_op && mem_range_softc.mr_op->initAP) mem_range_softc.mr_op->initAP(&mem_range_softc); } /* * Round up to the next power of two, if necessary, and then * take log2. * Returns -1 if argument is zero. */ static __inline int mask_width(u_int x) { return (fls(x << (1 - powerof2(x))) - 1); } /* * Add a cache level to the cache topology description. */ static int add_deterministic_cache(int type, int level, int share_count) { if (type == 0) return (0); if (type > 3) { printf("unexpected cache type %d\n", type); return (1); } if (type == 2) /* ignore instruction cache */ return (1); if (level == 0 || level > MAX_CACHE_LEVELS) { printf("unexpected cache level %d\n", type); return (1); } if (caches[level - 1].present) { printf("WARNING: multiple entries for L%u data cache\n", level); printf("%u => %u\n", caches[level - 1].id_shift, mask_width(share_count)); } caches[level - 1].id_shift = mask_width(share_count); caches[level - 1].present = 1; if (caches[level - 1].id_shift > pkg_id_shift) { printf("WARNING: L%u data cache covers more " "APIC IDs than a package (%u > %u)\n", level, caches[level - 1].id_shift, pkg_id_shift); caches[level - 1].id_shift = pkg_id_shift; } if (caches[level - 1].id_shift < core_id_shift) { printf("WARNING: L%u data cache covers fewer " "APIC IDs than a core (%u < %u)\n", level, caches[level - 1].id_shift, core_id_shift); caches[level - 1].id_shift = core_id_shift; } return (1); } /* * Determine topology of processing units and caches for AMD CPUs. * See: * - AMD CPUID Specification (Publication # 25481) * - BKDG for AMD NPT Family 0Fh Processors (Publication # 32559) * - BKDG For AMD Family 10h Processors (Publication # 31116) * - BKDG For AMD Family 15h Models 00h-0Fh Processors (Publication # 42301) * - BKDG For AMD Family 16h Models 00h-0Fh Processors (Publication # 48751) */ static void topo_probe_amd(void) { u_int p[4]; uint64_t v; int level; int nodes_per_socket; int share_count; int type; int i; /* No multi-core capability. */ if ((amd_feature2 & AMDID2_CMP) == 0) return; /* For families 10h and newer. */ pkg_id_shift = (cpu_procinfo2 & AMDID_COREID_SIZE) >> AMDID_COREID_SIZE_SHIFT; /* For 0Fh family. */ if (pkg_id_shift == 0) pkg_id_shift = mask_width((cpu_procinfo2 & AMDID_CMP_CORES) + 1); /* * Families prior to 16h define the following value as * cores per compute unit and we don't really care about the AMD * compute units at the moment. Perhaps we should treat them as * cores and cores within the compute units as hardware threads, * but that's up for debate. * Later families define the value as threads per compute unit, * so we are following AMD's nomenclature here. */ if ((amd_feature2 & AMDID2_TOPOLOGY) != 0 && CPUID_TO_FAMILY(cpu_id) >= 0x16) { cpuid_count(0x8000001e, 0, p); share_count = ((p[1] >> 8) & 0xff) + 1; core_id_shift = mask_width(share_count); /* * For Zen (17h), gather Nodes per Processor. Each node is a * Zeppelin die; TR and EPYC CPUs will have multiple dies per * package. Communication latency between dies is higher than * within them. */ nodes_per_socket = ((p[2] >> 8) & 0x7) + 1; node_id_shift = pkg_id_shift - mask_width(nodes_per_socket); } if ((amd_feature2 & AMDID2_TOPOLOGY) != 0) { for (i = 0; ; i++) { cpuid_count(0x8000001d, i, p); type = p[0] & 0x1f; level = (p[0] >> 5) & 0x7; share_count = 1 + ((p[0] >> 14) & 0xfff); if (!add_deterministic_cache(type, level, share_count)) break; } } else { if (cpu_exthigh >= 0x80000005) { cpuid_count(0x80000005, 0, p); if (((p[2] >> 24) & 0xff) != 0) { caches[0].id_shift = 0; caches[0].present = 1; } } if (cpu_exthigh >= 0x80000006) { cpuid_count(0x80000006, 0, p); if (((p[2] >> 16) & 0xffff) != 0) { caches[1].id_shift = 0; caches[1].present = 1; } if (((p[3] >> 18) & 0x3fff) != 0) { nodes_per_socket = 1; if ((amd_feature2 & AMDID2_NODE_ID) != 0) { /* * Handle multi-node processors that * have multiple chips, each with its * own L3 cache, on the same die. */ v = rdmsr(0xc001100c); nodes_per_socket = 1 + ((v >> 3) & 0x7); } caches[2].id_shift = pkg_id_shift - mask_width(nodes_per_socket); caches[2].present = 1; } } } } /* * Determine topology of processing units for Intel CPUs * using CPUID Leaf 1 and Leaf 4, if supported. * See: * - Intel 64 Architecture Processor Topology Enumeration * - Intel 64 and IA-32 ArchitecturesSoftware Developer’s Manual, * Volume 3A: System Programming Guide, PROGRAMMING CONSIDERATIONS * FOR HARDWARE MULTI-THREADING CAPABLE PROCESSORS */ static void topo_probe_intel_0x4(void) { u_int p[4]; int max_cores; int max_logical; /* Both zero and one here mean one logical processor per package. */ max_logical = (cpu_feature & CPUID_HTT) != 0 ? (cpu_procinfo & CPUID_HTT_CORES) >> 16 : 1; if (max_logical <= 1) return; if (cpu_high >= 0x4) { cpuid_count(0x04, 0, p); max_cores = ((p[0] >> 26) & 0x3f) + 1; } else max_cores = 1; core_id_shift = mask_width(max_logical/max_cores); KASSERT(core_id_shift >= 0, ("intel topo: max_cores > max_logical\n")); pkg_id_shift = core_id_shift + mask_width(max_cores); } /* * Determine topology of processing units for Intel CPUs * using CPUID Leaf 11, if supported. * See: * - Intel 64 Architecture Processor Topology Enumeration * - Intel 64 and IA-32 ArchitecturesSoftware Developer’s Manual, * Volume 3A: System Programming Guide, PROGRAMMING CONSIDERATIONS * FOR HARDWARE MULTI-THREADING CAPABLE PROCESSORS */ static void topo_probe_intel_0xb(void) { u_int p[4]; int bits; int type; int i; /* Fall back if CPU leaf 11 doesn't really exist. */ cpuid_count(0x0b, 0, p); if (p[1] == 0) { topo_probe_intel_0x4(); return; } /* We only support three levels for now. */ for (i = 0; ; i++) { cpuid_count(0x0b, i, p); bits = p[0] & 0x1f; type = (p[2] >> 8) & 0xff; if (type == 0) break; /* TODO: check for duplicate (re-)assignment */ if (type == CPUID_TYPE_SMT) core_id_shift = bits; else if (type == CPUID_TYPE_CORE) pkg_id_shift = bits; else printf("unknown CPU level type %d\n", type); } if (pkg_id_shift < core_id_shift) { printf("WARNING: core covers more APIC IDs than a package\n"); core_id_shift = pkg_id_shift; } } /* * Determine topology of caches for Intel CPUs. * See: * - Intel 64 Architecture Processor Topology Enumeration * - Intel 64 and IA-32 Architectures Software Developer’s Manual * Volume 2A: Instruction Set Reference, A-M, * CPUID instruction */ static void topo_probe_intel_caches(void) { u_int p[4]; int level; int share_count; int type; int i; if (cpu_high < 0x4) { /* * Available cache level and sizes can be determined * via CPUID leaf 2, but that requires a huge table of hardcoded * values, so for now just assume L1 and L2 caches potentially * shared only by HTT processing units, if HTT is present. */ caches[0].id_shift = pkg_id_shift; caches[0].present = 1; caches[1].id_shift = pkg_id_shift; caches[1].present = 1; return; } for (i = 0; ; i++) { cpuid_count(0x4, i, p); type = p[0] & 0x1f; level = (p[0] >> 5) & 0x7; share_count = 1 + ((p[0] >> 14) & 0xfff); if (!add_deterministic_cache(type, level, share_count)) break; } } /* * Determine topology of processing units and caches for Intel CPUs. * See: * - Intel 64 Architecture Processor Topology Enumeration */ static void topo_probe_intel(void) { /* * Note that 0x1 <= cpu_high < 4 case should be * compatible with topo_probe_intel_0x4() logic when * CPUID.1:EBX[23:16] > 0 (cpu_cores will be 1) * or it should trigger the fallback otherwise. */ if (cpu_high >= 0xb) topo_probe_intel_0xb(); else if (cpu_high >= 0x1) topo_probe_intel_0x4(); topo_probe_intel_caches(); } /* * Topology information is queried only on BSP, on which this * code runs and for which it can query CPUID information. * Then topology is extrapolated on all packages using an * assumption that APIC ID to hardware component ID mapping is * homogenious. * That doesn't necesserily imply that the topology is uniform. */ void topo_probe(void) { static int cpu_topo_probed = 0; struct x86_topo_layer { int type; int subtype; int id_shift; } topo_layers[MAX_CACHE_LEVELS + 4]; struct topo_node *parent; struct topo_node *node; int layer; int nlayers; int node_id; int i; if (cpu_topo_probed) return; CPU_ZERO(&logical_cpus_mask); if (mp_ncpus <= 1) ; /* nothing */ else if (cpu_vendor_id == CPU_VENDOR_AMD) topo_probe_amd(); else if (cpu_vendor_id == CPU_VENDOR_INTEL) topo_probe_intel(); KASSERT(pkg_id_shift >= core_id_shift, ("bug in APIC topology discovery")); nlayers = 0; bzero(topo_layers, sizeof(topo_layers)); topo_layers[nlayers].type = TOPO_TYPE_PKG; topo_layers[nlayers].id_shift = pkg_id_shift; if (bootverbose) printf("Package ID shift: %u\n", topo_layers[nlayers].id_shift); nlayers++; if (pkg_id_shift > node_id_shift && node_id_shift != 0) { topo_layers[nlayers].type = TOPO_TYPE_GROUP; topo_layers[nlayers].id_shift = node_id_shift; if (bootverbose) printf("Node ID shift: %u\n", topo_layers[nlayers].id_shift); nlayers++; } /* * Consider all caches to be within a package/chip * and "in front" of all sub-components like * cores and hardware threads. */ for (i = MAX_CACHE_LEVELS - 1; i >= 0; --i) { if (caches[i].present) { if (node_id_shift != 0) KASSERT(caches[i].id_shift <= node_id_shift, ("bug in APIC topology discovery")); KASSERT(caches[i].id_shift <= pkg_id_shift, ("bug in APIC topology discovery")); KASSERT(caches[i].id_shift >= core_id_shift, ("bug in APIC topology discovery")); topo_layers[nlayers].type = TOPO_TYPE_CACHE; topo_layers[nlayers].subtype = i + 1; topo_layers[nlayers].id_shift = caches[i].id_shift; if (bootverbose) printf("L%u cache ID shift: %u\n", topo_layers[nlayers].subtype, topo_layers[nlayers].id_shift); nlayers++; } } if (pkg_id_shift > core_id_shift) { topo_layers[nlayers].type = TOPO_TYPE_CORE; topo_layers[nlayers].id_shift = core_id_shift; if (bootverbose) printf("Core ID shift: %u\n", topo_layers[nlayers].id_shift); nlayers++; } topo_layers[nlayers].type = TOPO_TYPE_PU; topo_layers[nlayers].id_shift = 0; nlayers++; topo_init_root(&topo_root); for (i = 0; i <= max_apic_id; ++i) { if (!cpu_info[i].cpu_present) continue; parent = &topo_root; for (layer = 0; layer < nlayers; ++layer) { node_id = i >> topo_layers[layer].id_shift; parent = topo_add_node_by_hwid(parent, node_id, topo_layers[layer].type, topo_layers[layer].subtype); } } parent = &topo_root; for (layer = 0; layer < nlayers; ++layer) { node_id = boot_cpu_id >> topo_layers[layer].id_shift; node = topo_find_node_by_hwid(parent, node_id, topo_layers[layer].type, topo_layers[layer].subtype); topo_promote_child(node); parent = node; } cpu_topo_probed = 1; } /* * Assign logical CPU IDs to local APICs. */ void assign_cpu_ids(void) { struct topo_node *node; u_int smt_mask; smt_mask = (1u << core_id_shift) - 1; /* * Assign CPU IDs to local APIC IDs and disable any CPUs * beyond MAXCPU. CPU 0 is always assigned to the BSP. */ mp_ncpus = 0; TOPO_FOREACH(node, &topo_root) { if (node->type != TOPO_TYPE_PU) continue; if ((node->hwid & smt_mask) != (boot_cpu_id & smt_mask)) cpu_info[node->hwid].cpu_hyperthread = 1; if (resource_disabled("lapic", node->hwid)) { if (node->hwid != boot_cpu_id) cpu_info[node->hwid].cpu_disabled = 1; else printf("Cannot disable BSP, APIC ID = %d\n", node->hwid); } if (!hyperthreading_allowed && cpu_info[node->hwid].cpu_hyperthread) cpu_info[node->hwid].cpu_disabled = 1; if (mp_ncpus >= MAXCPU) cpu_info[node->hwid].cpu_disabled = 1; if (cpu_info[node->hwid].cpu_disabled) { disabled_cpus++; continue; } cpu_apic_ids[mp_ncpus] = node->hwid; apic_cpuids[node->hwid] = mp_ncpus; topo_set_pu_id(node, mp_ncpus); mp_ncpus++; } KASSERT(mp_maxid >= mp_ncpus - 1, ("%s: counters out of sync: max %d, count %d", __func__, mp_maxid, mp_ncpus)); } /* * Print various information about the SMP system hardware and setup. */ void cpu_mp_announce(void) { struct topo_node *node; const char *hyperthread; struct topo_analysis topology; printf("FreeBSD/SMP: "); if (topo_analyze(&topo_root, 1, &topology)) { printf("%d package(s)", topology.entities[TOPO_LEVEL_PKG]); if (topology.entities[TOPO_LEVEL_GROUP] > 1) printf(" x %d groups", topology.entities[TOPO_LEVEL_GROUP]); if (topology.entities[TOPO_LEVEL_CACHEGROUP] > 1) printf(" x %d cache groups", topology.entities[TOPO_LEVEL_CACHEGROUP]); if (topology.entities[TOPO_LEVEL_CORE] > 0) printf(" x %d core(s)", topology.entities[TOPO_LEVEL_CORE]); if (topology.entities[TOPO_LEVEL_THREAD] > 1) printf(" x %d hardware threads", topology.entities[TOPO_LEVEL_THREAD]); } else { printf("Non-uniform topology"); } printf("\n"); if (disabled_cpus) { printf("FreeBSD/SMP Online: "); if (topo_analyze(&topo_root, 0, &topology)) { printf("%d package(s)", topology.entities[TOPO_LEVEL_PKG]); if (topology.entities[TOPO_LEVEL_GROUP] > 1) printf(" x %d groups", topology.entities[TOPO_LEVEL_GROUP]); if (topology.entities[TOPO_LEVEL_CACHEGROUP] > 1) printf(" x %d cache groups", topology.entities[TOPO_LEVEL_CACHEGROUP]); if (topology.entities[TOPO_LEVEL_CORE] > 0) printf(" x %d core(s)", topology.entities[TOPO_LEVEL_CORE]); if (topology.entities[TOPO_LEVEL_THREAD] > 1) printf(" x %d hardware threads", topology.entities[TOPO_LEVEL_THREAD]); } else { printf("Non-uniform topology"); } printf("\n"); } if (!bootverbose) return; TOPO_FOREACH(node, &topo_root) { switch (node->type) { case TOPO_TYPE_PKG: printf("Package HW ID = %u\n", node->hwid); break; case TOPO_TYPE_CORE: printf("\tCore HW ID = %u\n", node->hwid); break; case TOPO_TYPE_PU: if (cpu_info[node->hwid].cpu_hyperthread) hyperthread = "/HT"; else hyperthread = ""; if (node->subtype == 0) printf("\t\tCPU (AP%s): APIC ID: %u" "(disabled)\n", hyperthread, node->hwid); else if (node->id == 0) printf("\t\tCPU0 (BSP): APIC ID: %u\n", node->hwid); else printf("\t\tCPU%u (AP%s): APIC ID: %u\n", node->id, hyperthread, node->hwid); break; default: /* ignored */ break; } } } /* * Add a scheduling group, a group of logical processors sharing * a particular cache (and, thus having an affinity), to the scheduling * topology. * This function recursively works on lower level caches. */ static void x86topo_add_sched_group(struct topo_node *root, struct cpu_group *cg_root) { struct topo_node *node; int nchildren; int ncores; int i; KASSERT(root->type == TOPO_TYPE_SYSTEM || root->type == TOPO_TYPE_CACHE || root->type == TOPO_TYPE_GROUP, ("x86topo_add_sched_group: bad type: %u", root->type)); CPU_COPY(&root->cpuset, &cg_root->cg_mask); cg_root->cg_count = root->cpu_count; if (root->type == TOPO_TYPE_SYSTEM) cg_root->cg_level = CG_SHARE_NONE; else cg_root->cg_level = root->subtype; /* * Check how many core nodes we have under the given root node. * If we have multiple logical processors, but not multiple * cores, then those processors must be hardware threads. */ ncores = 0; node = root; while (node != NULL) { if (node->type != TOPO_TYPE_CORE) { node = topo_next_node(root, node); continue; } ncores++; node = topo_next_nonchild_node(root, node); } if (cg_root->cg_level != CG_SHARE_NONE && root->cpu_count > 1 && ncores < 2) cg_root->cg_flags = CG_FLAG_SMT; /* * Find out how many cache nodes we have under the given root node. * We ignore cache nodes that cover all the same processors as the * root node. Also, we do not descend below found cache nodes. * That is, we count top-level "non-redundant" caches under the root * node. */ nchildren = 0; node = root; while (node != NULL) { if ((node->type != TOPO_TYPE_GROUP && node->type != TOPO_TYPE_CACHE) || (root->type != TOPO_TYPE_SYSTEM && CPU_CMP(&node->cpuset, &root->cpuset) == 0)) { node = topo_next_node(root, node); continue; } nchildren++; node = topo_next_nonchild_node(root, node); } cg_root->cg_child = smp_topo_alloc(nchildren); cg_root->cg_children = nchildren; /* * Now find again the same cache nodes as above and recursively * build scheduling topologies for them. */ node = root; i = 0; while (node != NULL) { if ((node->type != TOPO_TYPE_GROUP && node->type != TOPO_TYPE_CACHE) || (root->type != TOPO_TYPE_SYSTEM && CPU_CMP(&node->cpuset, &root->cpuset) == 0)) { node = topo_next_node(root, node); continue; } cg_root->cg_child[i].cg_parent = cg_root; x86topo_add_sched_group(node, &cg_root->cg_child[i]); i++; node = topo_next_nonchild_node(root, node); } } /* * Build the MI scheduling topology from the discovered hardware topology. */ struct cpu_group * cpu_topo(void) { struct cpu_group *cg_root; if (mp_ncpus <= 1) return (smp_topo_none()); cg_root = smp_topo_alloc(1); x86topo_add_sched_group(&topo_root, cg_root); return (cg_root); } static void cpu_alloc(void *dummy __unused) { /* * Dynamically allocate the arrays that depend on the * maximum APIC ID. */ cpu_info = malloc(sizeof(*cpu_info) * (max_apic_id + 1), M_CPUS, M_WAITOK | M_ZERO); apic_cpuids = malloc(sizeof(*apic_cpuids) * (max_apic_id + 1), M_CPUS, M_WAITOK | M_ZERO); } SYSINIT(cpu_alloc, SI_SUB_CPU, SI_ORDER_FIRST, cpu_alloc, NULL); /* * Add a logical CPU to the topology. */ void cpu_add(u_int apic_id, char boot_cpu) { if (apic_id > max_apic_id) { panic("SMP: APIC ID %d too high", apic_id); return; } KASSERT(cpu_info[apic_id].cpu_present == 0, ("CPU %u added twice", apic_id)); cpu_info[apic_id].cpu_present = 1; if (boot_cpu) { KASSERT(boot_cpu_id == -1, ("CPU %u claims to be BSP, but CPU %u already is", apic_id, boot_cpu_id)); boot_cpu_id = apic_id; cpu_info[apic_id].cpu_bsp = 1; } if (bootverbose) printf("SMP: Added CPU %u (%s)\n", apic_id, boot_cpu ? "BSP" : "AP"); } void cpu_mp_setmaxid(void) { /* * mp_ncpus and mp_maxid should be already set by calls to cpu_add(). * If there were no calls to cpu_add() assume this is a UP system. */ if (mp_ncpus == 0) mp_ncpus = 1; } int cpu_mp_probe(void) { /* * Always record BSP in CPU map so that the mbuf init code works * correctly. */ CPU_SETOF(0, &all_cpus); return (mp_ncpus > 1); } /* Allocate memory for the AP trampoline. */ void alloc_ap_trampoline(vm_paddr_t *physmap, unsigned int *physmap_idx) { unsigned int i; bool allocated; allocated = false; for (i = *physmap_idx; i <= *physmap_idx; i -= 2) { /* * Find a memory region big enough and below the 1MB boundary * for the trampoline code. * NB: needs to be page aligned. */ if (physmap[i] >= MiB(1) || (trunc_page(physmap[i + 1]) - round_page(physmap[i])) < round_page(bootMP_size)) continue; allocated = true; /* * Try to steal from the end of the region to mimic previous * behaviour, else fallback to steal from the start. */ if (physmap[i + 1] < MiB(1)) { boot_address = trunc_page(physmap[i + 1]); if ((physmap[i + 1] - boot_address) < bootMP_size) boot_address -= round_page(bootMP_size); physmap[i + 1] = boot_address; } else { boot_address = round_page(physmap[i]); physmap[i] = boot_address + round_page(bootMP_size); } if (physmap[i] == physmap[i + 1] && *physmap_idx != 0) { memmove(&physmap[i], &physmap[i + 2], sizeof(*physmap) * (*physmap_idx - i + 2)); *physmap_idx -= 2; } break; } if (!allocated) { boot_address = basemem * 1024 - bootMP_size; if (bootverbose) printf( "Cannot find enough space for the boot trampoline, placing it at %#x", boot_address); } } /* * AP CPU's call this to initialize themselves. */ void init_secondary_tail(void) { u_int cpuid; pmap_activate_boot(vmspace_pmap(proc0.p_vmspace)); /* * On real hardware, switch to x2apic mode if possible. Do it * after aps_ready was signalled, to avoid manipulating the * mode while BSP might still want to send some IPI to us * (second startup IPI is ignored on modern hardware etc). */ lapic_xapic_mode(); /* Initialize the PAT MSR. */ pmap_init_pat(); /* set up CPU registers and state */ cpu_setregs(); /* set up SSE/NX */ initializecpu(); /* set up FPU state on the AP */ #ifdef __amd64__ fpuinit(); #else npxinit(false); #endif if (cpu_ops.cpu_init) cpu_ops.cpu_init(); /* A quick check from sanity claus */ cpuid = PCPU_GET(cpuid); if (PCPU_GET(apic_id) != lapic_id()) { printf("SMP: cpuid = %d\n", cpuid); printf("SMP: actual apic_id = %d\n", lapic_id()); printf("SMP: correct apic_id = %d\n", PCPU_GET(apic_id)); panic("cpuid mismatch! boom!!"); } /* Initialize curthread. */ KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread")); PCPU_SET(curthread, PCPU_GET(idlethread)); mtx_lock_spin(&ap_boot_mtx); mca_init(); /* Init local apic for irq's */ lapic_setup(1); /* Set memory range attributes for this CPU to match the BSP */ mem_range_AP_init(); smp_cpus++; CTR1(KTR_SMP, "SMP: AP CPU #%d Launched", cpuid); if (bootverbose) printf("SMP: AP CPU #%d Launched!\n", cpuid); else printf("%s%d%s", smp_cpus == 2 ? "Launching APs: " : "", cpuid, smp_cpus == mp_ncpus ? "\n" : " "); /* Determine if we are a logical CPU. */ if (cpu_info[PCPU_GET(apic_id)].cpu_hyperthread) CPU_SET(cpuid, &logical_cpus_mask); if (bootverbose) lapic_dump("AP"); if (smp_cpus == mp_ncpus) { /* enable IPI's, tlb shootdown, freezes etc */ atomic_store_rel_int(&smp_started, 1); } #ifdef __amd64__ /* * Enable global pages TLB extension * This also implicitly flushes the TLB */ load_cr4(rcr4() | CR4_PGE); if (pmap_pcid_enabled) load_cr4(rcr4() | CR4_PCIDE); load_ds(_udatasel); load_es(_udatasel); load_fs(_ufssel); #endif mtx_unlock_spin(&ap_boot_mtx); /* Wait until all the AP's are up. */ while (atomic_load_acq_int(&smp_started) == 0) ia32_pause(); #ifndef EARLY_AP_STARTUP /* Start per-CPU event timers. */ cpu_initclocks_ap(); #endif sched_throw(NULL); panic("scheduler returned us to %s", __func__); /* NOTREACHED */ } -/******************************************************************* - * local functions and data - */ +static void +smp_after_idle_runnable(void *arg __unused) +{ + struct thread *idle_td; + int cpu; + + for (cpu = 1; cpu < mp_ncpus; cpu++) { + idle_td = pcpu_find(cpu)->pc_idlethread; + while (idle_td->td_lastcpu == NOCPU && + idle_td->td_oncpu == NOCPU) + cpu_spinwait(); + kmem_free((vm_offset_t)bootstacks[cpu], kstack_pages * + PAGE_SIZE); + } +} +SYSINIT(smp_after_idle_runnable, SI_SUB_SMP, SI_ORDER_ANY, + smp_after_idle_runnable, NULL); /* * We tell the I/O APIC code about all the CPUs we want to receive * interrupts. If we don't want certain CPUs to receive IRQs we * can simply not tell the I/O APIC code about them in this function. * We also do not tell it about the BSP since it tells itself about * the BSP internally to work with UP kernels and on UP machines. */ void set_interrupt_apic_ids(void) { u_int i, apic_id; for (i = 0; i < MAXCPU; i++) { apic_id = cpu_apic_ids[i]; if (apic_id == -1) continue; if (cpu_info[apic_id].cpu_bsp) continue; if (cpu_info[apic_id].cpu_disabled) continue; /* Don't let hyperthreads service interrupts. */ if (cpu_info[apic_id].cpu_hyperthread) continue; intr_add_cpu(i); } } #ifdef COUNT_XINVLTLB_HITS u_int xhits_gbl[MAXCPU]; u_int xhits_pg[MAXCPU]; u_int xhits_rng[MAXCPU]; static SYSCTL_NODE(_debug, OID_AUTO, xhits, CTLFLAG_RW, 0, ""); SYSCTL_OPAQUE(_debug_xhits, OID_AUTO, global, CTLFLAG_RW, &xhits_gbl, sizeof(xhits_gbl), "IU", ""); SYSCTL_OPAQUE(_debug_xhits, OID_AUTO, page, CTLFLAG_RW, &xhits_pg, sizeof(xhits_pg), "IU", ""); SYSCTL_OPAQUE(_debug_xhits, OID_AUTO, range, CTLFLAG_RW, &xhits_rng, sizeof(xhits_rng), "IU", ""); u_int ipi_global; u_int ipi_page; u_int ipi_range; u_int ipi_range_size; SYSCTL_INT(_debug_xhits, OID_AUTO, ipi_global, CTLFLAG_RW, &ipi_global, 0, ""); SYSCTL_INT(_debug_xhits, OID_AUTO, ipi_page, CTLFLAG_RW, &ipi_page, 0, ""); SYSCTL_INT(_debug_xhits, OID_AUTO, ipi_range, CTLFLAG_RW, &ipi_range, 0, ""); SYSCTL_INT(_debug_xhits, OID_AUTO, ipi_range_size, CTLFLAG_RW, &ipi_range_size, 0, ""); #endif /* COUNT_XINVLTLB_HITS */ /* * Init and startup IPI. */ void ipi_startup(int apic_id, int vector) { /* * This attempts to follow the algorithm described in the * Intel Multiprocessor Specification v1.4 in section B.4. * For each IPI, we allow the local APIC ~20us to deliver the * IPI. If that times out, we panic. */ /* * first we do an INIT IPI: this INIT IPI might be run, resetting * and running the target CPU. OR this INIT IPI might be latched (P5 * bug), CPU waiting for STARTUP IPI. OR this INIT IPI might be * ignored. */ lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_LEVEL | APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id); lapic_ipi_wait(100); /* Explicitly deassert the INIT IPI. */ lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_LEVEL | APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id); DELAY(10000); /* wait ~10mS */ /* * next we do a STARTUP IPI: the previous INIT IPI might still be * latched, (P5 bug) this 1st STARTUP would then terminate * immediately, and the previously started INIT IPI would continue. OR * the previous INIT IPI has already run. and this STARTUP IPI will * run. OR the previous INIT IPI was ignored. and this STARTUP IPI * will run. */ lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE | APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP | vector, apic_id); if (!lapic_ipi_wait(100)) panic("Failed to deliver first STARTUP IPI to APIC %d", apic_id); DELAY(200); /* wait ~200uS */ /* * finally we do a 2nd STARTUP IPI: this 2nd STARTUP IPI should run IF * the previous STARTUP IPI was cancelled by a latched INIT IPI. OR * this STARTUP IPI will be ignored, as only ONE STARTUP IPI is * recognized after hardware RESET or INIT IPI. */ lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE | APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP | vector, apic_id); if (!lapic_ipi_wait(100)) panic("Failed to deliver second STARTUP IPI to APIC %d", apic_id); DELAY(200); /* wait ~200uS */ } /* * Send an IPI to specified CPU handling the bitmap logic. */ void ipi_send_cpu(int cpu, u_int ipi) { u_int bitmap, old_pending, new_pending; KASSERT(cpu_apic_ids[cpu] != -1, ("IPI to non-existent CPU %d", cpu)); if (IPI_IS_BITMAPED(ipi)) { bitmap = 1 << ipi; ipi = IPI_BITMAP_VECTOR; do { old_pending = cpu_ipi_pending[cpu]; new_pending = old_pending | bitmap; } while (!atomic_cmpset_int(&cpu_ipi_pending[cpu], old_pending, new_pending)); if (old_pending) return; } lapic_ipi_vectored(ipi, cpu_apic_ids[cpu]); } void ipi_bitmap_handler(struct trapframe frame) { struct trapframe *oldframe; struct thread *td; int cpu = PCPU_GET(cpuid); u_int ipi_bitmap; critical_enter(); td = curthread; td->td_intr_nesting_level++; oldframe = td->td_intr_frame; td->td_intr_frame = &frame; ipi_bitmap = atomic_readandclear_int(&cpu_ipi_pending[cpu]); if (ipi_bitmap & (1 << IPI_PREEMPT)) { #ifdef COUNT_IPIS (*ipi_preempt_counts[cpu])++; #endif sched_preempt(td); } if (ipi_bitmap & (1 << IPI_AST)) { #ifdef COUNT_IPIS (*ipi_ast_counts[cpu])++; #endif /* Nothing to do for AST */ } if (ipi_bitmap & (1 << IPI_HARDCLOCK)) { #ifdef COUNT_IPIS (*ipi_hardclock_counts[cpu])++; #endif hardclockintr(); } td->td_intr_frame = oldframe; td->td_intr_nesting_level--; critical_exit(); } /* * send an IPI to a set of cpus. */ void ipi_selected(cpuset_t cpus, u_int ipi) { int cpu; /* * IPI_STOP_HARD maps to a NMI and the trap handler needs a bit * of help in order to understand what is the source. * Set the mask of receiving CPUs for this purpose. */ if (ipi == IPI_STOP_HARD) CPU_OR_ATOMIC(&ipi_stop_nmi_pending, &cpus); while ((cpu = CPU_FFS(&cpus)) != 0) { cpu--; CPU_CLR(cpu, &cpus); CTR3(KTR_SMP, "%s: cpu: %d ipi: %x", __func__, cpu, ipi); ipi_send_cpu(cpu, ipi); } } /* * send an IPI to a specific CPU. */ void ipi_cpu(int cpu, u_int ipi) { /* * IPI_STOP_HARD maps to a NMI and the trap handler needs a bit * of help in order to understand what is the source. * Set the mask of receiving CPUs for this purpose. */ if (ipi == IPI_STOP_HARD) CPU_SET_ATOMIC(cpu, &ipi_stop_nmi_pending); CTR3(KTR_SMP, "%s: cpu: %d ipi: %x", __func__, cpu, ipi); ipi_send_cpu(cpu, ipi); } /* * send an IPI to all CPUs EXCEPT myself */ void ipi_all_but_self(u_int ipi) { cpuset_t other_cpus; other_cpus = all_cpus; CPU_CLR(PCPU_GET(cpuid), &other_cpus); if (IPI_IS_BITMAPED(ipi)) { ipi_selected(other_cpus, ipi); return; } /* * IPI_STOP_HARD maps to a NMI and the trap handler needs a bit * of help in order to understand what is the source. * Set the mask of receiving CPUs for this purpose. */ if (ipi == IPI_STOP_HARD) CPU_OR_ATOMIC(&ipi_stop_nmi_pending, &other_cpus); CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS); } int ipi_nmi_handler(void) { u_int cpuid; /* * As long as there is not a simple way to know about a NMI's * source, if the bitmask for the current CPU is present in * the global pending bitword an IPI_STOP_HARD has been issued * and should be handled. */ cpuid = PCPU_GET(cpuid); if (!CPU_ISSET(cpuid, &ipi_stop_nmi_pending)) return (1); CPU_CLR_ATOMIC(cpuid, &ipi_stop_nmi_pending); cpustop_handler(); return (0); } int nmi_kdb_lock; void nmi_call_kdb_smp(u_int type, struct trapframe *frame) { int cpu; bool call_post; cpu = PCPU_GET(cpuid); if (atomic_cmpset_acq_int(&nmi_kdb_lock, 0, 1)) { nmi_call_kdb(cpu, type, frame); call_post = false; } else { savectx(&stoppcbs[cpu]); CPU_SET_ATOMIC(cpu, &stopped_cpus); while (!atomic_cmpset_acq_int(&nmi_kdb_lock, 0, 1)) ia32_pause(); call_post = true; } atomic_store_rel_int(&nmi_kdb_lock, 0); if (call_post) cpustop_handler_post(cpu); } /* * Handle an IPI_STOP by saving our current context and spinning until we * are resumed. */ void cpustop_handler(void) { u_int cpu; cpu = PCPU_GET(cpuid); savectx(&stoppcbs[cpu]); /* Indicate that we are stopped */ CPU_SET_ATOMIC(cpu, &stopped_cpus); /* Wait for restart */ while (!CPU_ISSET(cpu, &started_cpus)) ia32_pause(); cpustop_handler_post(cpu); } static void cpustop_handler_post(u_int cpu) { CPU_CLR_ATOMIC(cpu, &started_cpus); CPU_CLR_ATOMIC(cpu, &stopped_cpus); /* * We don't broadcast TLB invalidations to other CPUs when they are * stopped. Hence, we clear the TLB before resuming. */ invltlb_glob(); #if defined(__amd64__) && defined(DDB) amd64_db_resume_dbreg(); #endif if (cpu == 0 && cpustop_restartfunc != NULL) { cpustop_restartfunc(); cpustop_restartfunc = NULL; } } /* * Handle an IPI_SUSPEND by saving our current context and spinning until we * are resumed. */ void cpususpend_handler(void) { u_int cpu; mtx_assert(&smp_ipi_mtx, MA_NOTOWNED); cpu = PCPU_GET(cpuid); if (savectx(&susppcbs[cpu]->sp_pcb)) { #ifdef __amd64__ fpususpend(susppcbs[cpu]->sp_fpususpend); #else npxsuspend(susppcbs[cpu]->sp_fpususpend); #endif /* * suspended_cpus is cleared shortly after each AP is restarted * by a Startup IPI, so that the BSP can proceed to restarting * the next AP. * * resuming_cpus gets cleared when the AP completes * initialization after having been released by the BSP. * resuming_cpus is probably not the best name for the * variable, because it is actually a set of processors that * haven't resumed yet and haven't necessarily started resuming. * * Note that suspended_cpus is meaningful only for ACPI suspend * as it's not really used for Xen suspend since the APs are * automatically restored to the running state and the correct * context. For the same reason resumectx is never called in * that case. */ CPU_SET_ATOMIC(cpu, &suspended_cpus); CPU_SET_ATOMIC(cpu, &resuming_cpus); /* * Invalidate the cache after setting the global status bits. * The last AP to set its bit may end up being an Owner of the * corresponding cache line in MOESI protocol. The AP may be * stopped before the cache line is written to the main memory. */ wbinvd(); } else { #ifdef __amd64__ fpuresume(susppcbs[cpu]->sp_fpususpend); #else npxresume(susppcbs[cpu]->sp_fpususpend); #endif pmap_init_pat(); initializecpu(); PCPU_SET(switchtime, 0); PCPU_SET(switchticks, ticks); /* Indicate that we have restarted and restored the context. */ CPU_CLR_ATOMIC(cpu, &suspended_cpus); } /* Wait for resume directive */ while (!CPU_ISSET(cpu, &toresume_cpus)) ia32_pause(); /* Re-apply microcode updates. */ ucode_reload(); #ifdef __i386__ /* Finish removing the identity mapping of low memory for this AP. */ invltlb_glob(); #endif if (cpu_ops.cpu_resume) cpu_ops.cpu_resume(); #ifdef __amd64__ if (vmm_resume_p) vmm_resume_p(); #endif /* Resume MCA and local APIC */ lapic_xapic_mode(); mca_resume(); lapic_setup(0); /* Indicate that we are resumed */ CPU_CLR_ATOMIC(cpu, &resuming_cpus); CPU_CLR_ATOMIC(cpu, &suspended_cpus); CPU_CLR_ATOMIC(cpu, &toresume_cpus); } void invlcache_handler(void) { uint32_t generation; #ifdef COUNT_IPIS (*ipi_invlcache_counts[PCPU_GET(cpuid)])++; #endif /* COUNT_IPIS */ /* * Reading the generation here allows greater parallelism * since wbinvd is a serializing instruction. Without the * temporary, we'd wait for wbinvd to complete, then the read * would execute, then the dependent write, which must then * complete before return from interrupt. */ generation = smp_tlb_generation; wbinvd(); PCPU_SET(smp_tlb_done, generation); } /* * This is called once the rest of the system is up and running and we're * ready to let the AP's out of the pen. */ static void release_aps(void *dummy __unused) { if (mp_ncpus == 1) return; atomic_store_rel_int(&aps_ready, 1); while (smp_started == 0) ia32_pause(); } SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, release_aps, NULL); #ifdef COUNT_IPIS /* * Setup interrupt counters for IPI handlers. */ static void mp_ipi_intrcnt(void *dummy) { char buf[64]; int i; CPU_FOREACH(i) { snprintf(buf, sizeof(buf), "cpu%d:invltlb", i); intrcnt_add(buf, &ipi_invltlb_counts[i]); snprintf(buf, sizeof(buf), "cpu%d:invlrng", i); intrcnt_add(buf, &ipi_invlrng_counts[i]); snprintf(buf, sizeof(buf), "cpu%d:invlpg", i); intrcnt_add(buf, &ipi_invlpg_counts[i]); snprintf(buf, sizeof(buf), "cpu%d:invlcache", i); intrcnt_add(buf, &ipi_invlcache_counts[i]); snprintf(buf, sizeof(buf), "cpu%d:preempt", i); intrcnt_add(buf, &ipi_preempt_counts[i]); snprintf(buf, sizeof(buf), "cpu%d:ast", i); intrcnt_add(buf, &ipi_ast_counts[i]); snprintf(buf, sizeof(buf), "cpu%d:rendezvous", i); intrcnt_add(buf, &ipi_rendezvous_counts[i]); snprintf(buf, sizeof(buf), "cpu%d:hardclock", i); intrcnt_add(buf, &ipi_hardclock_counts[i]); } } SYSINIT(mp_ipi_intrcnt, SI_SUB_INTR, SI_ORDER_MIDDLE, mp_ipi_intrcnt, NULL); #endif /* * Flush the TLB on other CPU's */ /* Variables needed for SMP tlb shootdown. */ vm_offset_t smp_tlb_addr1, smp_tlb_addr2; pmap_t smp_tlb_pmap; volatile uint32_t smp_tlb_generation; #ifdef __amd64__ #define read_eflags() read_rflags() #endif static void smp_targeted_tlb_shootdown(cpuset_t mask, u_int vector, pmap_t pmap, vm_offset_t addr1, vm_offset_t addr2) { cpuset_t other_cpus; volatile uint32_t *p_cpudone; uint32_t generation; int cpu; /* It is not necessary to signal other CPUs while in the debugger. */ if (kdb_active || panicstr != NULL) return; /* * Check for other cpus. Return if none. */ if (CPU_ISFULLSET(&mask)) { if (mp_ncpus <= 1) return; } else { CPU_CLR(PCPU_GET(cpuid), &mask); if (CPU_EMPTY(&mask)) return; } if (!(read_eflags() & PSL_I)) panic("%s: interrupts disabled", __func__); mtx_lock_spin(&smp_ipi_mtx); smp_tlb_addr1 = addr1; smp_tlb_addr2 = addr2; smp_tlb_pmap = pmap; generation = ++smp_tlb_generation; if (CPU_ISFULLSET(&mask)) { ipi_all_but_self(vector); other_cpus = all_cpus; CPU_CLR(PCPU_GET(cpuid), &other_cpus); } else { other_cpus = mask; while ((cpu = CPU_FFS(&mask)) != 0) { cpu--; CPU_CLR(cpu, &mask); CTR3(KTR_SMP, "%s: cpu: %d ipi: %x", __func__, cpu, vector); ipi_send_cpu(cpu, vector); } } while ((cpu = CPU_FFS(&other_cpus)) != 0) { cpu--; CPU_CLR(cpu, &other_cpus); p_cpudone = &cpuid_to_pcpu[cpu]->pc_smp_tlb_done; while (*p_cpudone != generation) ia32_pause(); } mtx_unlock_spin(&smp_ipi_mtx); } void smp_masked_invltlb(cpuset_t mask, pmap_t pmap) { if (smp_started) { smp_targeted_tlb_shootdown(mask, IPI_INVLTLB, pmap, 0, 0); #ifdef COUNT_XINVLTLB_HITS ipi_global++; #endif } } void smp_masked_invlpg(cpuset_t mask, vm_offset_t addr, pmap_t pmap) { if (smp_started) { smp_targeted_tlb_shootdown(mask, IPI_INVLPG, pmap, addr, 0); #ifdef COUNT_XINVLTLB_HITS ipi_page++; #endif } } void smp_masked_invlpg_range(cpuset_t mask, vm_offset_t addr1, vm_offset_t addr2, pmap_t pmap) { if (smp_started) { smp_targeted_tlb_shootdown(mask, IPI_INVLRNG, pmap, addr1, addr2); #ifdef COUNT_XINVLTLB_HITS ipi_range++; ipi_range_size += (addr2 - addr1) / PAGE_SIZE; #endif } } void smp_cache_flush(void) { if (smp_started) { smp_targeted_tlb_shootdown(all_cpus, IPI_INVLCACHE, NULL, 0, 0); } } /* * Handlers for TLB related IPIs */ void invltlb_handler(void) { uint32_t generation; #ifdef COUNT_XINVLTLB_HITS xhits_gbl[PCPU_GET(cpuid)]++; #endif /* COUNT_XINVLTLB_HITS */ #ifdef COUNT_IPIS (*ipi_invltlb_counts[PCPU_GET(cpuid)])++; #endif /* COUNT_IPIS */ /* * Reading the generation here allows greater parallelism * since invalidating the TLB is a serializing operation. */ generation = smp_tlb_generation; if (smp_tlb_pmap == kernel_pmap) invltlb_glob(); #ifdef __amd64__ else invltlb(); #endif PCPU_SET(smp_tlb_done, generation); } void invlpg_handler(void) { uint32_t generation; #ifdef COUNT_XINVLTLB_HITS xhits_pg[PCPU_GET(cpuid)]++; #endif /* COUNT_XINVLTLB_HITS */ #ifdef COUNT_IPIS (*ipi_invlpg_counts[PCPU_GET(cpuid)])++; #endif /* COUNT_IPIS */ generation = smp_tlb_generation; /* Overlap with serialization */ #ifdef __i386__ if (smp_tlb_pmap == kernel_pmap) #endif invlpg(smp_tlb_addr1); PCPU_SET(smp_tlb_done, generation); } void invlrng_handler(void) { vm_offset_t addr, addr2; uint32_t generation; #ifdef COUNT_XINVLTLB_HITS xhits_rng[PCPU_GET(cpuid)]++; #endif /* COUNT_XINVLTLB_HITS */ #ifdef COUNT_IPIS (*ipi_invlrng_counts[PCPU_GET(cpuid)])++; #endif /* COUNT_IPIS */ addr = smp_tlb_addr1; addr2 = smp_tlb_addr2; generation = smp_tlb_generation; /* Overlap with serialization */ #ifdef __i386__ if (smp_tlb_pmap == kernel_pmap) #endif do { invlpg(addr); addr += PAGE_SIZE; } while (addr < addr2); PCPU_SET(smp_tlb_done, generation); } Index: projects/clang700-import/tests/sys/kern/ptrace_test.c =================================================================== --- projects/clang700-import/tests/sys/kern/ptrace_test.c (revision 341812) +++ projects/clang700-import/tests/sys/kern/ptrace_test.c (revision 341813) @@ -1,3836 +1,3909 @@ /*- * Copyright (c) 2015 John Baldwin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Architectures with a user-visible breakpoint(). */ #if defined(__aarch64__) || defined(__amd64__) || defined(__arm__) || \ defined(__i386__) || defined(__mips__) || defined(__riscv) || \ defined(__sparc64__) #define HAVE_BREAKPOINT #endif /* * Adjust PC to skip over a breakpoint when stopped for a breakpoint trap. */ #ifdef HAVE_BREAKPOINT #if defined(__aarch64__) #define SKIP_BREAK(reg) ((reg)->elr += 4) #elif defined(__amd64__) || defined(__i386__) #define SKIP_BREAK(reg) #elif defined(__arm__) #define SKIP_BREAK(reg) ((reg)->r_pc += 4) #elif defined(__mips__) #define SKIP_BREAK(reg) ((reg)->r_regs[PC] += 4) #elif defined(__riscv) #define SKIP_BREAK(reg) ((reg)->sepc += 4) #elif defined(__sparc64__) #define SKIP_BREAK(reg) do { \ (reg)->r_tpc = (reg)->r_tnpc + 4; \ (reg)->r_tnpc += 8; \ } while (0) #endif #endif /* * A variant of ATF_REQUIRE that is suitable for use in child * processes. This only works if the parent process is tripped up by * the early exit and fails some requirement itself. */ #define CHILD_REQUIRE(exp) do { \ if (!(exp)) \ child_fail_require(__FILE__, __LINE__, \ #exp " not met"); \ } while (0) static __dead2 void child_fail_require(const char *file, int line, const char *str) { char buf[128]; snprintf(buf, sizeof(buf), "%s:%d: %s\n", file, line, str); write(2, buf, strlen(buf)); _exit(32); } static void trace_me(void) { /* Attach the parent process as a tracer of this process. */ CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); /* Trigger a stop. */ raise(SIGSTOP); } static void attach_child(pid_t pid) { pid_t wpid; int status; ATF_REQUIRE(ptrace(PT_ATTACH, pid, NULL, 0) == 0); wpid = waitpid(pid, &status, 0); ATF_REQUIRE(wpid == pid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); } static void wait_for_zombie(pid_t pid) { /* * Wait for a process to exit. This is kind of gross, but * there is not a better way. * * Prior to r325719, the kern.proc.pid. sysctl failed * with ESRCH. After that change, a valid struct kinfo_proc * is returned for zombies with ki_stat set to SZOMB. */ for (;;) { struct kinfo_proc kp; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = pid; len = sizeof(kp); if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) { ATF_REQUIRE(errno == ESRCH); break; } if (kp.ki_stat == SZOMB) break; usleep(5000); } } /* * Verify that a parent debugger process "sees" the exit of a debugged * process exactly once when attached via PT_TRACE_ME. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me); ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc) { pid_t child, wpid; int status; ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ trace_me(); _exit(1); } /* Parent process. */ /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* The second wait() should report the exit status. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); /* The child should no longer exist. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a parent debugger process "sees" the exit of a debugged * process exactly once when attached via PT_ATTACH. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach); ATF_TC_BODY(ptrace__parent_wait_after_attach, tc) { pid_t child, wpid; int cpipe[2], status; char c; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for the parent to attach. */ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == 0); _exit(1); } close(cpipe[1]); /* Parent process. */ /* Attach to the child process. */ attach_child(child); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal the child to exit. */ close(cpipe[0]); /* The second wait() should report the exit status. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); /* The child should no longer exist. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a parent process "sees" the exit of a debugged process only * after the debugger has seen it. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_child_debugger); ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger, tc) { pid_t child, debugger, wpid; int cpipe[2], dpipe[2], status; char c; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c)); _exit(1); } close(cpipe[1]); ATF_REQUIRE(pipe(dpipe) == 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c)); /* Wait for parent's failed wait. */ CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == 0); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE(WEXITSTATUS(status) == 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger to attach to the child. */ ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c)); /* Release the child. */ ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c)); ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0); close(cpipe[0]); wait_for_zombie(child); /* * This wait should return a pid of 0 to indicate no status to * report. The parent should see the child as non-exited * until the debugger sees the exit. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == 0); /* Signal the debugger to wait for the child. */ close(dpipe[0]); /* Wait for the debugger. */ wpid = waitpid(debugger, &status, 0); ATF_REQUIRE(wpid == debugger); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); } /* * Verify that a parent process "sees" the exit of a debugged process * only after a non-direct-child debugger has seen it. In particular, * various wait() calls in the parent must avoid failing with ESRCH by * checking the parent's orphan list for the debugee. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_unrelated_debugger); ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc) { pid_t child, debugger, fpid, wpid; int cpipe[2], dpipe[2], status; char c; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c)); _exit(1); } close(cpipe[1]); ATF_REQUIRE(pipe(dpipe) == 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger parent. */ /* * Fork again and drop the debugger parent so that the * debugger is not a child of the main parent. */ CHILD_REQUIRE((fpid = fork()) != -1); if (fpid != 0) _exit(2); /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c)); /* Wait for parent's failed wait. */ CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == sizeof(c)); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE(WEXITSTATUS(status) == 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger parent process to exit. */ wpid = waitpid(debugger, &status, 0); ATF_REQUIRE(wpid == debugger); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); /* A WNOHANG wait here should see the non-exited child. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == 0); /* Wait for the debugger to attach to the child. */ ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c)); /* Release the child. */ ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c)); ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0); close(cpipe[0]); wait_for_zombie(child); /* * This wait should return a pid of 0 to indicate no status to * report. The parent should see the child as non-exited * until the debugger sees the exit. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == 0); /* Signal the debugger to wait for the child. */ ATF_REQUIRE(write(dpipe[0], &c, sizeof(c)) == sizeof(c)); /* Wait for the debugger. */ ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == 0); close(dpipe[0]); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); } /* * The parent process should always act the same regardless of how the * debugger is attached to it. */ static __dead2 void follow_fork_parent(bool use_vfork) { pid_t fpid, wpid; int status; if (use_vfork) CHILD_REQUIRE((fpid = vfork()) != -1); else CHILD_REQUIRE((fpid = fork()) != -1); if (fpid == 0) /* Child */ _exit(2); wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE(wpid == fpid); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE(WEXITSTATUS(status) == 2); _exit(1); } /* * Helper routine for follow fork tests. This waits for two stops * that report both "sides" of a fork. It returns the pid of the new * child process. */ static pid_t handle_fork_events(pid_t parent, struct ptrace_lwpinfo *ppl) { struct ptrace_lwpinfo pl; bool fork_reported[2]; pid_t child, wpid; int i, status; fork_reported[0] = false; fork_reported[1] = false; child = -1; /* * Each process should report a fork event. The parent should * report a PL_FLAG_FORKED event, and the child should report * a PL_FLAG_CHILD event. */ for (i = 0; i < 2; i++) { wpid = wait(&status); ATF_REQUIRE(wpid > 0); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_FORKED | PL_FLAG_CHILD)) != 0); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_FORKED | PL_FLAG_CHILD)) != (PL_FLAG_FORKED | PL_FLAG_CHILD)); if (pl.pl_flags & PL_FLAG_CHILD) { ATF_REQUIRE(wpid != parent); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(!fork_reported[1]); if (child == -1) child = wpid; else ATF_REQUIRE(child == wpid); if (ppl != NULL) ppl[1] = pl; fork_reported[1] = true; } else { ATF_REQUIRE(wpid == parent); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(!fork_reported[0]); if (child == -1) child = pl.pl_child_pid; else ATF_REQUIRE(child == pl.pl_child_pid); if (ppl != NULL) ppl[0] = pl; fork_reported[0] = true; } } return (child); } /* * Verify that a new child process is stopped after a followed fork and * that the traced parent sees the exit of the child after the debugger * when both processes remain attached to the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_both_attached); ATF_TC_BODY(ptrace__follow_fork_both_attached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the new * child process is detached after it reports its fork. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_child_detached); ATF_TC_BODY(ptrace__follow_fork_child_detached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the grandchild now, only the * child. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the * traced parent is detached after the fork. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_parent_detached); ATF_TC_BODY(ptrace__follow_fork_parent_detached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. * * Even though the child process is detached, it is still a * child of the debugger, so it will still report it's exit * after the grandchild. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void attach_fork_parent(int cpipe[2]) { pid_t fpid; close(cpipe[0]); /* Double-fork to disassociate from the debugger. */ CHILD_REQUIRE((fpid = fork()) != -1); if (fpid != 0) _exit(3); /* Send the pid of the disassociated child to the debugger. */ fpid = getpid(); CHILD_REQUIRE(write(cpipe[1], &fpid, sizeof(fpid)) == sizeof(fpid)); /* Wait for the debugger to attach. */ CHILD_REQUIRE(read(cpipe[1], &fpid, sizeof(fpid)) == 0); } /* * Verify that a new child process is stopped after a followed fork and * that the traced parent sees the exit of the child after the debugger * when both processes remain attached to the debugger. In this test * the parent that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_both_attached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_both_attached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 3); /* Read the pid of the fork parent. */ ATF_REQUIRE(read(cpipe[0], &children[0], sizeof(children[0])) == sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The fork parent can't exit until the child reports status, * so the child should report its exit first to the debugger. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the new * child process is detached after it reports its fork. In this test * the parent that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_child_detached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_child_detached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 3); /* Read the pid of the fork parent. */ ATF_REQUIRE(read(cpipe[0], &children[0], sizeof(children[0])) == sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the child now, only the fork * parent. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the * traced parent is detached after the fork. In this test the parent * that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_parent_detached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_parent_detached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 3); /* Read the pid of the fork parent. */ ATF_REQUIRE(read(cpipe[0], &children[0], sizeof(children[0])) == sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the fork parent now, only * the child. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a child process does not see an unrelated debugger as its * parent but sees its original parent process. */ ATF_TC_WITHOUT_HEAD(ptrace__getppid); ATF_TC_BODY(ptrace__getppid, tc) { pid_t child, debugger, ppid, wpid; int cpipe[2], dpipe[2], status; char c; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c)); /* Report the parent PID to the parent. */ ppid = getppid(); CHILD_REQUIRE(write(cpipe[1], &ppid, sizeof(ppid)) == sizeof(ppid)); _exit(1); } close(cpipe[1]); ATF_REQUIRE(pipe(dpipe) == 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c)); /* Wait for traced child to exit. */ wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE(WEXITSTATUS(status) == 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger to attach to the child. */ ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c)); /* Release the child. */ ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c)); /* Read the parent PID from the child. */ ATF_REQUIRE(read(cpipe[0], &ppid, sizeof(ppid)) == sizeof(ppid)); close(cpipe[0]); ATF_REQUIRE(ppid == getpid()); /* Wait for the debugger. */ wpid = waitpid(debugger, &status, 0); ATF_REQUIRE(wpid == debugger); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * child process created via fork() reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_fork); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_fork, tc) { struct ptrace_lwpinfo pl[2]; pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Wait for both halves of the fork event to get reported. */ children[1] = handle_fork_events(children[0], pl); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE(pl[0].pl_syscall_code == SYS_fork); ATF_REQUIRE(pl[0].pl_syscall_code == pl[1].pl_syscall_code); ATF_REQUIRE(pl[0].pl_syscall_narg == pl[1].pl_syscall_narg); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * child process created via vfork() reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_vfork); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_vfork, tc) { struct ptrace_lwpinfo pl[2]; pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(true); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Wait for both halves of the fork event to get reported. */ children[1] = handle_fork_events(children[0], pl); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE(pl[0].pl_syscall_code == SYS_vfork); ATF_REQUIRE(pl[0].pl_syscall_code == pl[1].pl_syscall_code); ATF_REQUIRE(pl[0].pl_syscall_narg == pl[1].pl_syscall_narg); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * simple_thread(void *arg __unused) { pthread_exit(NULL); } static __dead2 void simple_thread_main(void) { pthread_t thread; CHILD_REQUIRE(pthread_create(&thread, NULL, simple_thread, NULL) == 0); CHILD_REQUIRE(pthread_join(thread, NULL) == 0); exit(1); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * thread reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_thread); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t mainlwp; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); mainlwp = pl.pl_lwpid; /* * Continue the child ignoring the SIGSTOP and tracing all * system call exits. */ ATF_REQUIRE(ptrace(PT_TO_SCX, fpid, (caddr_t)1, 0) != -1); /* * Wait for the new thread to arrive. pthread_create() might * invoke any number of system calls. For now we just wait * for the new thread to arrive and make sure it reports a * valid system call code. If ptrace grows thread event * reporting then this test can be made more precise. */ for (;;) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE(pl.pl_syscall_code != 0); if (pl.pl_lwpid != mainlwp) /* New thread seen. */ break; ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } /* Wait for the child to exit. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); for (;;) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFEXITED(status)) break; ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that the expected LWP events are reported for a child thread. */ ATF_TC_WITHOUT_HEAD(ptrace__lwp_events); ATF_TC_BODY(ptrace__lwp_events, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t lwps[2]; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); lwps[0] = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)) == (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != lwps[0]); lwps[1] = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next event should be for the child thread's death. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_EXITED | PL_FLAG_SCE)) == (PL_FLAG_EXITED | PL_FLAG_SCE)); ATF_REQUIRE(pl.pl_lwpid == lwps[1]); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * exec_thread(void *arg __unused) { execl("/usr/bin/true", "true", NULL); exit(127); } static __dead2 void exec_thread_main(void) { pthread_t thread; CHILD_REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0); for (;;) sleep(60); exit(1); } /* * Verify that the expected LWP events are reported for a multithreaded * process that calls execve(2). */ ATF_TC_WITHOUT_HEAD(ptrace__lwp_events_exec); ATF_TC_BODY(ptrace__lwp_events_exec, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t lwps[2]; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); lwps[0] = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)) == (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != lwps[0]); lwps[1] = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* * The next event should be for the main thread's death due to * single threading from execve(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_EXITED | PL_FLAG_SCE)) == (PL_FLAG_EXITED)); ATF_REQUIRE(pl.pl_lwpid == lwps[0]); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next event should be for the child process's exec. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) == (PL_FLAG_EXEC | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid == lwps[1]); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void handler(int sig __unused) { } static void signal_main(void) { signal(SIGINFO, handler); raise(SIGINFO); exit(0); } /* * Verify that the expected ptrace event is reported for a signal. */ ATF_TC_WITHOUT_HEAD(ptrace__siginfo); ATF_TC_BODY(ptrace__siginfo, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); signal_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next event should be for the SIGINFO. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGINFO); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_event == PL_EVENT_SIGNAL); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_code == SI_LWP); ATF_REQUIRE(pl.pl_siginfo.si_pid == wpid); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that the expected ptrace events are reported for PTRACE_EXEC. */ ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_disable); ATF_TC_BODY(ptrace__ptrace_exec_disable, tc) { pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread(NULL); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); events = 0; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Should get one event at exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_enable); ATF_TC_BODY(ptrace__ptrace_exec_enable, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread(NULL); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); events = PTRACE_EXEC; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next event should be for the child process's exec. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) == (PL_FLAG_EXEC | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } ATF_TC_WITHOUT_HEAD(ptrace__event_mask); ATF_TC_BODY(ptrace__event_mask, tc) { pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(0); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* PT_FOLLOW_FORK should toggle the state of PTRACE_FORK. */ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 1) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(events & PTRACE_FORK); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 0) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(!(events & PTRACE_FORK)); /* PT_LWP_EVENTS should toggle the state of PTRACE_LWP. */ ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 1) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(events & PTRACE_LWP); ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 0) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(!(events & PTRACE_LWP)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Should get one event at exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that the expected ptrace events are reported for PTRACE_VFORK. */ ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork); ATF_TC_BODY(ptrace__ptrace_vfork, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(true); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); events |= PTRACE_VFORK; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1); /* The next event should report the end of the vfork. */ wpid = wait(&status); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_VFORK_DONE) != 0); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1); wpid = wait(&status); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork_follow); ATF_TC_BODY(ptrace__ptrace_vfork_follow, tc) { struct ptrace_lwpinfo pl[2]; pid_t children[2], fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(true); } /* Parent process. */ children[0] = fpid; /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(children[0], &status, 0); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, children[0], (caddr_t)&events, sizeof(events)) == 0); events |= PTRACE_FORK | PTRACE_VFORK; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, children[0], (caddr_t)&events, sizeof(events)) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Wait for both halves of the fork event to get reported. */ children[1] = handle_fork_events(children[0], pl); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORKED) != 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * The child can't exit until the grandchild reports status, so the * grandchild should report its exit first to the debugger. */ wpid = waitpid(children[1], &status, 0); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); /* * The child should report it's vfork() completion before it * exits. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl[0], sizeof(pl[0])) != -1); ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORK_DONE) != 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); wpid = wait(&status); ATF_REQUIRE(wpid == children[0]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } #ifdef HAVE_BREAKPOINT /* * Verify that no more events are reported after PT_KILL except for the * process exit when stopped due to a breakpoint trap. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_breakpoint); ATF_TC_BODY(ptrace__PT_KILL_breakpoint, tc) { pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); breakpoint(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report hitting the breakpoint. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } #endif /* HAVE_BREAKPOINT */ /* * Verify that no more events are reported after PT_KILL except for the * process exit when stopped inside of a system call. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_system_call); ATF_TC_BODY(ptrace__PT_KILL_system_call, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that no more events are reported after PT_KILL except for the * process exit when killing a multithreaded process. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_threads); ATF_TC_BODY(ptrace__PT_KILL_threads, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t main_lwp; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); main_lwp = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)) == (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != main_lwp); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * mask_usr1_thread(void *arg) { pthread_barrier_t *pbarrier; sigset_t sigmask; pbarrier = (pthread_barrier_t*)arg; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR1); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(pbarrier); for (;;) sleep(60); return (NULL); } /* * Verify that the SIGKILL from PT_KILL takes priority over other signals * and prevents spurious stops due to those other signals. */ ATF_TC(ptrace__PT_KILL_competing_signal); ATF_TC_HEAD(ptrace__PT_KILL_competing_signal, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(ptrace__PT_KILL_competing_signal, tc) { pid_t fpid, wpid; int status; cpuset_t setmask; pthread_t t; pthread_barrier_t barrier; struct sched_param sched_param; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { /* Bind to one CPU so only one thread at a time will run. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); cpusetid_t setid; CHILD_REQUIRE(cpuset(&setid) == 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); CHILD_REQUIRE(pthread_barrier_init(&barrier, NULL, 2) == 0); CHILD_REQUIRE(pthread_create(&t, NULL, mask_usr1_thread, (void*)&barrier) == 0); /* * Give the main thread higher priority. The test always * assumes that, if both threads are able to run, the main * thread runs first. */ sched_param.sched_priority = (sched_get_priority_max(SCHED_FIFO) + sched_get_priority_min(SCHED_FIFO)) / 2; CHILD_REQUIRE(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param) == 0); sched_param.sched_priority -= RQ_PPQ; CHILD_REQUIRE(pthread_setschedparam(t, SCHED_FIFO, &sched_param) == 0); sigset_t sigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR2); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(&barrier); trace_me(); for (;;) sleep(60); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Send a signal that only the second thread can handle. */ ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* The second wait() should report the SIGUSR2. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); /* Send a signal that only the first thread can handle. */ ATF_REQUIRE(kill(fpid, SIGUSR1) == 0); /* Replace the SIGUSR2 with a kill. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL (not the SIGUSR signal). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that the SIGKILL from PT_KILL takes priority over other stop events * and prevents spurious stops caused by those events. */ ATF_TC(ptrace__PT_KILL_competing_stop); ATF_TC_HEAD(ptrace__PT_KILL_competing_stop, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(ptrace__PT_KILL_competing_stop, tc) { pid_t fpid, wpid; int status; cpuset_t setmask; pthread_t t; pthread_barrier_t barrier; lwpid_t main_lwp; struct ptrace_lwpinfo pl; struct sched_param sched_param; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); /* Bind to one CPU so only one thread at a time will run. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); cpusetid_t setid; CHILD_REQUIRE(cpuset(&setid) == 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); CHILD_REQUIRE(pthread_barrier_init(&barrier, NULL, 2) == 0); CHILD_REQUIRE(pthread_create(&t, NULL, mask_usr1_thread, (void*)&barrier) == 0); /* * Give the main thread higher priority. The test always * assumes that, if both threads are able to run, the main * thread runs first. */ sched_param.sched_priority = (sched_get_priority_max(SCHED_FIFO) + sched_get_priority_min(SCHED_FIFO)) / 2; CHILD_REQUIRE(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param) == 0); sched_param.sched_priority -= RQ_PPQ; CHILD_REQUIRE(pthread_setschedparam(t, SCHED_FIFO, &sched_param) == 0); sigset_t sigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR2); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(&barrier); /* Sync up with the test before doing the getpid(). */ raise(SIGSTOP); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); main_lwp = pl.pl_lwpid; /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* * Continue until child is done with setup, which is indicated with * SIGSTOP. Ignore system calls in the meantime. */ for (;;) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); if (WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); } else { ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); break; } ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); } /* Proceed, allowing main thread to hit syscall entry for getpid(). */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_lwpid == main_lwp); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Prevent the main thread from hitting its syscall exit for now. */ ATF_REQUIRE(ptrace(PT_SUSPEND, main_lwp, 0, 0) == 0); /* * Proceed, allowing second thread to hit syscall exit for * pthread_barrier_wait(). */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_lwpid != main_lwp); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); /* Send a signal that only the second thread can handle. */ ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The next wait() should report the SIGUSR2. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); /* Allow the main thread to try to finish its system call. */ ATF_REQUIRE(ptrace(PT_RESUME, main_lwp, 0, 0) == 0); /* * At this point, the main thread is in the middle of a system call and * has been resumed. The second thread has taken a SIGUSR2 which will * be replaced with a SIGKILL below. The main thread will get to run * first. It should notice the kill request (even though the signal * replacement occurred in the other thread) and exit accordingly. It * should not stop for the system call exit event. */ /* Replace the SIGUSR2 with a kill. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL (not a syscall exit). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void sigusr1_handler(int sig) { CHILD_REQUIRE(sig == SIGUSR1); _exit(2); } /* * Verify that even if the signal queue is full for a child process, * a PT_KILL will kill the process. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_with_signal_full_sigqueue); ATF_TC_BODY(ptrace__PT_KILL_with_signal_full_sigqueue, tc) { pid_t fpid, wpid; int status; int max_pending_per_proc; size_t len; int i; ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); len = sizeof(max_pending_per_proc); ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc", &max_pending_per_proc, &len, NULL, 0) == 0); /* Fill the signal queue. */ for (i = 0; i < max_pending_per_proc; ++i) ATF_REQUIRE(kill(fpid, SIGUSR1) == 0); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that when stopped at a system call entry, a signal can be * requested with PT_CONTINUE which will be delivered once the system * call is complete. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_system_call_entry); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_system_call_entry, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Continue the child process with a signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void sigusr1_counting_handler(int sig) { static int counter = 0; CHILD_REQUIRE(sig == SIGUSR1); counter++; if (counter == 2) _exit(2); } /* * Verify that, when continuing from a stop at system call entry and exit, * a signal can be requested from both stops, and both will be delivered when * the system call is complete. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_counting_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Continue the child process with a signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The third wait() should report a system call exit for getpid(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); /* Continue the child process with a signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that even if the signal queue is full for a child process, * a PT_CONTINUE with a signal will not result in loss of that signal. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_full_sigqueue); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_full_sigqueue, tc) { pid_t fpid, wpid; int status; int max_pending_per_proc; size_t len; int i; ATF_REQUIRE(signal(SIGUSR2, handler) != SIG_ERR); ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); len = sizeof(max_pending_per_proc); ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc", &max_pending_per_proc, &len, NULL, 0) == 0); /* Fill the signal queue. */ for (i = 0; i < max_pending_per_proc; ++i) ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* Continue with signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); for (;;) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status)) { ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { /* * The last wait() should report normal _exit from the * SIGUSR1 handler. */ ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static sem_t sigusr1_sem; static int got_usr1; static void sigusr1_sempost_handler(int sig __unused) { got_usr1++; CHILD_REQUIRE(sem_post(&sigusr1_sem) == 0); } /* * Verify that even if the signal queue is full for a child process, * and the signal is masked, a PT_CONTINUE with a signal will not * result in loss of that signal. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, err; int max_pending_per_proc; size_t len; int i; sigset_t sigmask; ATF_REQUIRE(signal(SIGUSR2, handler) != SIG_ERR); ATF_REQUIRE(sem_init(&sigusr1_sem, 0, 0) == 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); got_usr1 = 0; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE(sigemptyset(&sigmask) == 0); CHILD_REQUIRE(sigaddset(&sigmask, SIGUSR1) == 0); CHILD_REQUIRE(sigprocmask(SIG_BLOCK, &sigmask, NULL) == 0); trace_me(); CHILD_REQUIRE(got_usr1 == 0); /* Allow the pending SIGUSR1 in now. */ CHILD_REQUIRE(sigprocmask(SIG_UNBLOCK, &sigmask, NULL) == 0); /* Wait to receive the SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); CHILD_REQUIRE(got_usr1 == 1); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); len = sizeof(max_pending_per_proc); ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc", &max_pending_per_proc, &len, NULL, 0) == 0); /* Fill the signal queue. */ for (i = 0; i < max_pending_per_proc; ++i) ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* Continue with signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* Collect and ignore all of the SIGUSR2. */ for (i = 0; i < max_pending_per_proc; ++i) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } /* Now our PT_CONTINUE'd SIGUSR1 should cause a stop after unmask. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR1); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGUSR1); /* Continue the child, ignoring the SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last wait() should report exit after receiving SIGUSR1. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that, after stopping due to a signal, that signal can be * replaced with another signal. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_change_sig); ATF_TC_BODY(ptrace__PT_CONTINUE_change_sig, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); sleep(20); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Send a signal without ptrace. */ ATF_REQUIRE(kill(fpid, SIGINT) == 0); /* The second wait() should report a SIGINT was received. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGINT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGINT); /* Continue the child process with a different signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGTERM) == 0); /* * The last wait() should report having died due to the new * signal, SIGTERM. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGTERM); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a signal can be passed through to the child even when there * was no true signal originally. Such cases arise when a SIGTRAP is * invented for e.g, system call stops. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_sigtrap_system_call_entry); ATF_TC_BODY(ptrace__PT_CONTINUE_with_sigtrap_system_call_entry, tc) { struct ptrace_lwpinfo pl; struct rlimit rl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); /* SIGTRAP expected to cause exit on syscall entry. */ rl.rlim_cur = rl.rlim_max = 0; ATF_REQUIRE(setrlimit(RLIMIT_CORE, &rl) == 0); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Continue the child process with a SIGTRAP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGTRAP) == 0); for (;;) { /* * The last wait() should report exit due to SIGTRAP. In the * meantime, catch and proceed past any syscall stops. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGTRAP); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * A mixed bag PT_CONTINUE with signal test. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_mix); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_mix, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_counting_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Continue with the first SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The next wait() should report a system call exit for getpid(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); /* Send an ABRT without ptrace. */ ATF_REQUIRE(kill(fpid, SIGABRT) == 0); /* Continue normally. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next wait() should report the SIGABRT. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGABRT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGABRT); /* Continue, replacing the SIGABRT with another SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify a signal delivered by ptrace is noticed by kevent(2). */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_kqueue); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_kqueue, tc) { pid_t fpid, wpid; int status, kq, nevents; struct kevent kev; ATF_REQUIRE(signal(SIGUSR1, SIG_IGN) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE((kq = kqueue()) > 0); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); CHILD_REQUIRE(kevent(kq, &kev, 1, NULL, 0, NULL) == 0); trace_me(); for (;;) { nevents = kevent(kq, NULL, 0, &kev, 1, NULL); if (nevents == -1 && errno == EINTR) continue; CHILD_REQUIRE(nevents > 0); CHILD_REQUIRE(kev.filter == EVFILT_SIGNAL); CHILD_REQUIRE(kev.ident == SIGUSR1); break; } exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue with the SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* * The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * signal_thread(void *arg) { int err; sigset_t sigmask; pthread_barrier_t *pbarrier = (pthread_barrier_t*)arg; /* Wait for this thread to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); /* Free our companion thread from the barrier. */ pthread_barrier_wait(pbarrier); /* * Swap ignore duties; the next SIGUSR1 should go to the * other thread. */ CHILD_REQUIRE(sigemptyset(&sigmask) == 0); CHILD_REQUIRE(sigaddset(&sigmask, SIGUSR1) == 0); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); /* Sync up threads after swapping signal masks. */ pthread_barrier_wait(pbarrier); /* Wait until our companion has received its SIGUSR1. */ pthread_barrier_wait(pbarrier); return (NULL); } /* * Verify that a traced process with blocked signal received the * signal from kill() once unmasked. */ ATF_TC_WITHOUT_HEAD(ptrace__killed_with_sigmask); ATF_TC_BODY(ptrace__killed_with_sigmask, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, err; sigset_t sigmask; ATF_REQUIRE(sem_init(&sigusr1_sem, 0, 0) == 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); got_usr1 = 0; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE(sigemptyset(&sigmask) == 0); CHILD_REQUIRE(sigaddset(&sigmask, SIGUSR1) == 0); CHILD_REQUIRE(sigprocmask(SIG_BLOCK, &sigmask, NULL) == 0); trace_me(); CHILD_REQUIRE(got_usr1 == 0); /* Allow the pending SIGUSR1 in now. */ CHILD_REQUIRE(sigprocmask(SIG_UNBLOCK, &sigmask, NULL) == 0); /* Wait to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); CHILD_REQUIRE(got_usr1 == 1); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGSTOP); /* Send blocked SIGUSR1 which should cause a stop. */ ATF_REQUIRE(kill(fpid, SIGUSR1) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next wait() should report the kill(SIGUSR1) was received. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR1); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGUSR1); /* Continue the child, allowing in the SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a traced process with blocked signal received the * signal from PT_CONTINUE once unmasked. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_sigmask); ATF_TC_BODY(ptrace__PT_CONTINUE_with_sigmask, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, err; sigset_t sigmask; ATF_REQUIRE(sem_init(&sigusr1_sem, 0, 0) == 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); got_usr1 = 0; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE(sigemptyset(&sigmask) == 0); CHILD_REQUIRE(sigaddset(&sigmask, SIGUSR1) == 0); CHILD_REQUIRE(sigprocmask(SIG_BLOCK, &sigmask, NULL) == 0); trace_me(); CHILD_REQUIRE(got_usr1 == 0); /* Allow the pending SIGUSR1 in now. */ CHILD_REQUIRE(sigprocmask(SIG_UNBLOCK, &sigmask, NULL) == 0); /* Wait to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); CHILD_REQUIRE(got_usr1 == 1); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGSTOP); /* Continue the child replacing SIGSTOP with SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The next wait() should report the SIGUSR1 was received. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR1); ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGUSR1); /* Continue the child, ignoring the SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that if ptrace stops due to a signal but continues with * a different signal that the new signal is routed to a thread * that can accept it, and that the thread is awakened by the signal * in a timely manner. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_thread_sigmask); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_thread_sigmask, tc) { pid_t fpid, wpid; int status, err; pthread_t t; sigset_t sigmask; pthread_barrier_t barrier; ATF_REQUIRE(pthread_barrier_init(&barrier, NULL, 2) == 0); ATF_REQUIRE(sem_init(&sigusr1_sem, 0, 0) == 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE(pthread_create(&t, NULL, signal_thread, (void*)&barrier) == 0); /* The other thread should receive the first SIGUSR1. */ CHILD_REQUIRE(sigemptyset(&sigmask) == 0); CHILD_REQUIRE(sigaddset(&sigmask, SIGUSR1) == 0); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); trace_me(); /* Wait until other thread has received its SIGUSR1. */ pthread_barrier_wait(&barrier); /* * Swap ignore duties; the next SIGUSR1 should go to this * thread. */ CHILD_REQUIRE(pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL) == 0); /* Sync up threads after swapping signal masks. */ pthread_barrier_wait(&barrier); /* * Sync up with test code; we're ready for the next SIGUSR1 * now. */ raise(SIGSTOP); /* Wait for this thread to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); /* Free the other thread from the barrier. */ pthread_barrier_wait(&barrier); CHILD_REQUIRE(pthread_join(t, NULL) == 0); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* * Send a signal without ptrace that either thread will accept (USR2, * in this case). */ ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* The second wait() should report a SIGUSR2 was received. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); /* Continue the child, changing the signal to USR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The next wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* The next wait() should report a SIGUSR2 was received. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); /* Continue the child, changing the signal to USR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * raise_sigstop_thread(void *arg __unused) { raise(SIGSTOP); return NULL; } static void * sleep_thread(void *arg __unused) { sleep(60); return NULL; } static void terminate_with_pending_sigstop(bool sigstop_from_main_thread) { pid_t fpid, wpid; int status, i; cpuset_t setmask; cpusetid_t setid; pthread_t t; /* * Become the reaper for this process tree. We need to be able to check * that both child and grandchild have died. */ ATF_REQUIRE(procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) == 0); fpid = fork(); ATF_REQUIRE(fpid >= 0); if (fpid == 0) { fpid = fork(); CHILD_REQUIRE(fpid >= 0); if (fpid == 0) { trace_me(); /* Pin to CPU 0 to serialize thread execution. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); CHILD_REQUIRE(cpuset(&setid) == 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); if (sigstop_from_main_thread) { /* * We expect the SIGKILL sent when our parent * dies to be delivered to the new thread. * Raise the SIGSTOP in this thread so the * threads compete. */ CHILD_REQUIRE(pthread_create(&t, NULL, sleep_thread, NULL) == 0); raise(SIGSTOP); } else { /* * We expect the SIGKILL to be delivered to * this thread. After creating the new thread, * just get off the CPU so the other thread can * raise the SIGSTOP. */ CHILD_REQUIRE(pthread_create(&t, NULL, raise_sigstop_thread, NULL) == 0); sleep(60); } exit(0); } /* First stop is trace_me() immediately after fork. */ wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE(wpid == fpid); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Second stop is from the raise(SIGSTOP). */ wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE(wpid == fpid); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* * Terminate tracing process without detaching. Our child * should be killed. */ exit(0); } /* * We should get a normal exit from our immediate child and a SIGKILL * exit from our grandchild. The latter case is the interesting one. * Our grandchild should not have stopped due to the SIGSTOP that was * left dangling when its parent died. */ for (i = 0; i < 2; ++i) { wpid = wait(&status); if (wpid == fpid) ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 0); else ATF_REQUIRE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL); } } /* * These two tests ensure that if the tracing process exits without detaching * just after the child received a SIGSTOP, the child is cleanly killed and * doesn't go to sleep due to the SIGSTOP. The parent's death will send a * SIGKILL to the child. If the SIGKILL and the SIGSTOP are handled by * different threads, the SIGKILL must win. There are two variants of this * test, designed to catch the case where the SIGKILL is delivered to the * younger thread (the first test) and the case where the SIGKILL is delivered * to the older thread (the second test). This behavior has changed in the * past, so make no assumption. */ ATF_TC(ptrace__parent_terminate_with_pending_sigstop1); ATF_TC_HEAD(ptrace__parent_terminate_with_pending_sigstop1, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop1, tc) { terminate_with_pending_sigstop(true); } ATF_TC(ptrace__parent_terminate_with_pending_sigstop2); ATF_TC_HEAD(ptrace__parent_terminate_with_pending_sigstop2, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop2, tc) { terminate_with_pending_sigstop(false); } /* * Verify that after ptrace() discards a SIGKILL signal, the event mask * is not modified. */ ATF_TC_WITHOUT_HEAD(ptrace__event_mask_sigkill_discard); ATF_TC_BODY(ptrace__event_mask_sigkill_discard, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, event_mask, new_event_mask; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); raise(SIGSTOP); exit(0); } /* The first wait() should report the stop from trace_me(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Set several unobtrusive event bits. */ event_mask = PTRACE_EXEC | PTRACE_FORK | PTRACE_LWP; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, wpid, (caddr_t)&event_mask, sizeof(event_mask)) == 0); /* Send a SIGKILL without using ptrace. */ ATF_REQUIRE(kill(fpid, SIGKILL) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next stop should be due to the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGKILL); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGKILL); /* Continue the child ignoring the SIGKILL. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Check the current event mask. It should not have changed. */ new_event_mask = 0; ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, wpid, (caddr_t)&new_event_mask, sizeof(new_event_mask)) == 0); ATF_REQUIRE(event_mask == new_event_mask); /* Continue the child to let it exit. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * flock_thread(void *arg) { int fd; fd = *(int *)arg; (void)flock(fd, LOCK_EX); (void)flock(fd, LOCK_UN); return (NULL); } /* * Verify that PT_ATTACH will suspend threads sleeping in an SBDRY section. * We rely on the fact that the lockf implementation sets SBDRY before blocking * on a lock. This is a regression test for r318191. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_ATTACH_with_SBDRY_thread); ATF_TC_BODY(ptrace__PT_ATTACH_with_SBDRY_thread, tc) { pthread_barrier_t barrier; pthread_barrierattr_t battr; char tmpfile[64]; pid_t child, wpid; int error, fd, i, status; ATF_REQUIRE(pthread_barrierattr_init(&battr) == 0); ATF_REQUIRE(pthread_barrierattr_setpshared(&battr, PTHREAD_PROCESS_SHARED) == 0); ATF_REQUIRE(pthread_barrier_init(&barrier, &battr, 2) == 0); (void)snprintf(tmpfile, sizeof(tmpfile), "./ptrace.XXXXXX"); fd = mkstemp(tmpfile); ATF_REQUIRE(fd >= 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { pthread_t t[2]; int cfd; error = pthread_barrier_wait(&barrier); if (error != 0 && error != PTHREAD_BARRIER_SERIAL_THREAD) _exit(1); cfd = open(tmpfile, O_RDONLY); if (cfd < 0) _exit(1); /* * We want at least two threads blocked on the file lock since * the SIGSTOP from PT_ATTACH may kick one of them out of * sleep. */ if (pthread_create(&t[0], NULL, flock_thread, &cfd) != 0) _exit(1); if (pthread_create(&t[1], NULL, flock_thread, &cfd) != 0) _exit(1); if (pthread_join(t[0], NULL) != 0) _exit(1); if (pthread_join(t[1], NULL) != 0) _exit(1); _exit(0); } ATF_REQUIRE(flock(fd, LOCK_EX) == 0); error = pthread_barrier_wait(&barrier); ATF_REQUIRE(error == 0 || error == PTHREAD_BARRIER_SERIAL_THREAD); /* * Give the child some time to block. Is there a better way to do this? */ sleep(1); /* * Attach and give the child 3 seconds to stop. */ ATF_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) == 0); for (i = 0; i < 3; i++) { wpid = waitpid(child, &status, WNOHANG); if (wpid == child && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) break; sleep(1); } ATF_REQUIRE_MSG(i < 3, "failed to stop child process after PT_ATTACH"); ATF_REQUIRE(ptrace(PT_DETACH, child, NULL, 0) == 0); ATF_REQUIRE(flock(fd, LOCK_UN) == 0); ATF_REQUIRE(unlink(tmpfile) == 0); ATF_REQUIRE(close(fd) == 0); } static void sigusr1_step_handler(int sig) { CHILD_REQUIRE(sig == SIGUSR1); raise(SIGABRT); } /* * Verify that PT_STEP with a signal invokes the signal before * stepping the next instruction (and that the next instruction is * stepped correctly). */ ATF_TC_WITHOUT_HEAD(ptrace__PT_STEP_with_signal); ATF_TC_BODY(ptrace__PT_STEP_with_signal, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); signal(SIGUSR1, sigusr1_step_handler); raise(SIGABRT); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next stop should report the SIGABRT in the child body. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGABRT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGABRT); /* Step the child process inserting SIGUSR1. */ ATF_REQUIRE(ptrace(PT_STEP, fpid, (caddr_t)1, SIGUSR1) == 0); /* The next stop should report the SIGABRT in the signal handler. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGABRT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGABRT); /* Continue the child process discarding the signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next stop should report a trace trap from PT_STEP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGTRAP); ATF_REQUIRE(pl.pl_siginfo.si_code == TRAP_TRACE); /* Continue the child to let it exit. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } #ifdef HAVE_BREAKPOINT /* * Verify that a SIGTRAP event with the TRAP_BRKPT code is reported * for a breakpoint trap. */ ATF_TC_WITHOUT_HEAD(ptrace__breakpoint_siginfo); ATF_TC_BODY(ptrace__breakpoint_siginfo, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); breakpoint(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report hitting the breakpoint. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGTRAP); ATF_REQUIRE(pl.pl_siginfo.si_code == TRAP_BRKPT); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } #endif /* HAVE_BREAKPOINT */ /* * Verify that a SIGTRAP event with the TRAP_TRACE code is reported * for a single-step trap from PT_STEP. */ ATF_TC_WITHOUT_HEAD(ptrace__step_siginfo); ATF_TC_BODY(ptrace__step_siginfo, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(1); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Step the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_STEP, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a single-step trap. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGTRAP); ATF_REQUIRE(pl.pl_siginfo.si_code == TRAP_TRACE); /* Continue the child process. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } #if defined(HAVE_BREAKPOINT) && defined(SKIP_BREAK) static void * continue_thread(void *arg __unused) { breakpoint(); return (NULL); } static __dead2 void continue_thread_main(void) { pthread_t threads[2]; CHILD_REQUIRE(pthread_create(&threads[0], NULL, continue_thread, NULL) == 0); CHILD_REQUIRE(pthread_create(&threads[1], NULL, continue_thread, NULL) == 0); CHILD_REQUIRE(pthread_join(threads[0], NULL) == 0); CHILD_REQUIRE(pthread_join(threads[1], NULL) == 0); exit(1); } /* * Ensure that PT_CONTINUE clears the status of the thread that * triggered the stop even if a different thread's LWP was passed to * PT_CONTINUE. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_different_thread); ATF_TC_BODY(ptrace__PT_CONTINUE_different_thread, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t lwps[2]; bool hit_break[2]; struct reg reg; int i, j, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); continue_thread_main(); } /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* One of the new threads should report it's birth. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)) == (PL_FLAG_BORN | PL_FLAG_SCX)); lwps[0] = pl.pl_lwpid; /* * Suspend this thread to ensure both threads are alive before * hitting the breakpoint. */ ATF_REQUIRE(ptrace(PT_SUSPEND, lwps[0], NULL, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Second thread should report it's birth. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)) == (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != lwps[0]); lwps[1] = pl.pl_lwpid; /* Resume both threads waiting for breakpoint events. */ hit_break[0] = hit_break[1] = false; ATF_REQUIRE(ptrace(PT_RESUME, lwps[0], NULL, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* One thread should report a breakpoint. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGTRAP && pl.pl_siginfo.si_code == TRAP_BRKPT); if (pl.pl_lwpid == lwps[0]) i = 0; else i = 1; hit_break[i] = true; ATF_REQUIRE(ptrace(PT_GETREGS, pl.pl_lwpid, (caddr_t)®, 0) != -1); SKIP_BREAK(®); ATF_REQUIRE(ptrace(PT_SETREGS, pl.pl_lwpid, (caddr_t)®, 0) != -1); /* * Resume both threads but pass the other thread's LWPID to * PT_CONTINUE. */ ATF_REQUIRE(ptrace(PT_CONTINUE, lwps[i ^ 1], (caddr_t)1, 0) == 0); /* * Will now get two thread exit events and one more breakpoint * event. */ for (j = 0; j < 3; j++) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); if (pl.pl_lwpid == lwps[0]) i = 0; else i = 1; ATF_REQUIRE_MSG(lwps[i] != 0, "event for exited thread"); if (pl.pl_flags & PL_FLAG_EXITED) { ATF_REQUIRE_MSG(hit_break[i], "exited thread did not report breakpoint"); lwps[i] = 0; } else { ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGTRAP && pl.pl_siginfo.si_code == TRAP_BRKPT); ATF_REQUIRE_MSG(!hit_break[i], "double breakpoint event"); hit_break[i] = true; ATF_REQUIRE(ptrace(PT_GETREGS, pl.pl_lwpid, (caddr_t)®, 0) != -1); SKIP_BREAK(®); ATF_REQUIRE(ptrace(PT_SETREGS, pl.pl_lwpid, (caddr_t)®, 0) != -1); } ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } /* Both threads should have exited. */ ATF_REQUIRE(lwps[0] == 0); ATF_REQUIRE(lwps[1] == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } #endif +/* + * Verify that PT_LWPINFO doesn't return stale siginfo. + */ +ATF_TC_WITHOUT_HEAD(ptrace__PT_LWPINFO_stale_siginfo); +ATF_TC_BODY(ptrace__PT_LWPINFO_stale_siginfo, tc) +{ + struct ptrace_lwpinfo pl; + pid_t fpid, wpid; + int events, status; + + ATF_REQUIRE((fpid = fork()) != -1); + if (fpid == 0) { + trace_me(); + raise(SIGABRT); + exit(1); + } + + /* The first wait() should report the stop from SIGSTOP. */ + wpid = waitpid(fpid, &status, 0); + ATF_REQUIRE(wpid == fpid); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); + + ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); + + /* The next stop should report the SIGABRT in the child body. */ + wpid = waitpid(fpid, &status, 0); + ATF_REQUIRE(wpid == fpid); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGABRT); + + ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); + ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); + ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGABRT); + + /* + * Continue the process ignoring the signal, but enabling + * syscall traps. + */ + ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); + + /* + * The next stop should report a system call entry from + * exit(). PL_FLAGS_SI should not be set. + */ + wpid = waitpid(fpid, &status, 0); + ATF_REQUIRE(wpid == fpid); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); + + ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); + ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); + ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) == 0); + + /* Disable syscall tracing and continue the child to let it exit. */ + ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, + sizeof(events)) == 0); + events &= ~PTRACE_SYSCALL; + ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, + sizeof(events)) == 0); + ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); + + /* The last event should be for the child process's exit. */ + wpid = waitpid(fpid, &status, 0); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me); ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach); ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_child_debugger); ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_both_attached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_child_detached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_parent_detached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_both_attached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_child_detached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_parent_detached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__getppid); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_fork); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_vfork); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_thread); ATF_TP_ADD_TC(tp, ptrace__lwp_events); ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec); ATF_TP_ADD_TC(tp, ptrace__siginfo); ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_disable); ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_enable); ATF_TP_ADD_TC(tp, ptrace__event_mask); ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork); ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork_follow); #ifdef HAVE_BREAKPOINT ATF_TP_ADD_TC(tp, ptrace__PT_KILL_breakpoint); #endif ATF_TP_ADD_TC(tp, ptrace__PT_KILL_system_call); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_threads); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_competing_signal); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_competing_stop); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_with_signal_full_sigqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_system_call_entry); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_full_sigqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_change_sig); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigtrap_system_call_entry); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_mix); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_kqueue); ATF_TP_ADD_TC(tp, ptrace__killed_with_sigmask); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigmask); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_thread_sigmask); ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop1); ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop2); ATF_TP_ADD_TC(tp, ptrace__event_mask_sigkill_discard); ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_with_SBDRY_thread); ATF_TP_ADD_TC(tp, ptrace__PT_STEP_with_signal); #ifdef HAVE_BREAKPOINT ATF_TP_ADD_TC(tp, ptrace__breakpoint_siginfo); #endif ATF_TP_ADD_TC(tp, ptrace__step_siginfo); #if defined(HAVE_BREAKPOINT) && defined(SKIP_BREAK) ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_different_thread); #endif + ATF_TP_ADD_TC(tp, ptrace__PT_LWPINFO_stale_siginfo); return (atf_no_error()); } Index: projects/clang700-import/usr.bin/truss/main.c =================================================================== --- projects/clang700-import/usr.bin/truss/main.c (revision 341812) +++ projects/clang700-import/usr.bin/truss/main.c (revision 341813) @@ -1,206 +1,209 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright 1997 Sean Eric Fagan * * 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 Sean Eric Fagan * 4. Neither the name of the author 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. */ #include __FBSDID("$FreeBSD$"); /* * The main module for truss. Surprisingly simple, but, then, the other * files handle the bulk of the work. And, of course, the kernel has to * do a lot of the work :). */ #include #include #include #include #include #include #include #include #include #include "truss.h" #include "extern.h" #include "syscall.h" static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: truss [-cfaedDHS] [-o file] [-s strsize] -p pid", " truss [-cfaedDHS] [-o file] [-s strsize] command [args]"); exit(1); } int main(int ac, char **av) { struct sigaction sa; struct trussinfo *trussinfo; char *fname; char **command; + const char *errstr; pid_t pid; int c; fname = NULL; /* Initialize the trussinfo struct */ trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo)); if (trussinfo == NULL) errx(1, "calloc() failed"); pid = 0; trussinfo->outfile = stderr; trussinfo->strsize = 32; trussinfo->curthread = NULL; LIST_INIT(&trussinfo->proclist); init_syscalls(); while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) { switch (c) { case 'p': /* specified pid */ pid = atoi(optarg); /* make sure i don't trace me */ if (pid == getpid()) { errx(2, "attempt to grab self."); } break; case 'f': /* Follow fork()'s */ trussinfo->flags |= FOLLOWFORKS; break; case 'a': /* Print execve() argument strings. */ trussinfo->flags |= EXECVEARGS; break; case 'c': /* Count number of system calls and time. */ trussinfo->flags |= (COUNTONLY | NOSIGS); break; case 'e': /* Print execve() environment strings. */ trussinfo->flags |= EXECVEENVS; break; case 'd': /* Absolute timestamps */ trussinfo->flags |= ABSOLUTETIMESTAMPS; break; case 'D': /* Relative timestamps */ trussinfo->flags |= RELATIVETIMESTAMPS; break; case 'o': /* Specified output file */ fname = optarg; break; case 's': /* Specified string size */ - trussinfo->strsize = atoi(optarg); + trussinfo->strsize = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr) + errx(1, "maximum string size is %s: %s", errstr, optarg); break; case 'S': /* Don't trace signals */ trussinfo->flags |= NOSIGS; break; case 'H': trussinfo->flags |= DISPLAYTIDS; break; default: usage(); } } ac -= optind; av += optind; if ((pid == 0 && ac == 0) || (pid != 0 && ac != 0)) usage(); if (fname != NULL) { /* Use output file */ /* * Set close-on-exec ('e'), so that the output file is not * shared with the traced process. */ if ((trussinfo->outfile = fopen(fname, "we")) == NULL) err(1, "cannot open %s", fname); } /* * If truss starts the process itself, it will ignore some signals -- * they should be passed off to the process, which may or may not * exit. If, however, we are examining an already-running process, * then we restore the event mask on these same signals. */ if (pid == 0) { /* Start a command ourselves */ command = av; setup_and_wait(trussinfo, command); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); } else { sa.sa_handler = restore_proc; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); start_tracing(trussinfo, pid); } /* * At this point, if we started the process, it is stopped waiting to * be woken up, either in exit() or in execve(). */ if (LIST_FIRST(&trussinfo->proclist)->abi == NULL) { /* * If we are not able to handle this ABI, detach from the * process and exit. If we just created a new process to * run a command, kill the new process rather than letting * it run untraced. */ if (pid == 0) kill(LIST_FIRST(&trussinfo->proclist)->pid, SIGKILL); ptrace(PT_DETACH, LIST_FIRST(&trussinfo->proclist)->pid, NULL, 0); return (1); } ptrace(PT_SYSCALL, LIST_FIRST(&trussinfo->proclist)->pid, (caddr_t)1, 0); /* * At this point, it's a simple loop, waiting for the process to * stop, finding out why, printing out why, and then continuing it. * All of the grunt work is done in the support routines. */ clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); eventloop(trussinfo); if (trussinfo->flags & COUNTONLY) print_summary(trussinfo); fflush(trussinfo->outfile); return (0); } Index: projects/clang700-import =================================================================== --- projects/clang700-import (revision 341812) +++ projects/clang700-import (revision 341813) Property changes on: projects/clang700-import ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r341764-341812