summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason King <jasonbking@users.noreply.github.com>2020-01-14 09:57:36 -0600
committerGitHub <noreply@github.com>2020-01-14 09:57:36 -0600
commit81b0b97522c9ef54fd7ebae67bd982589417d656 (patch)
tree156c6754d5ce25731af47401a02cbdcad798054b
parent8ec7a75c64b22baa6f1eea820d7c751da8259a68 (diff)
parentd55d490bff89f9cf4f0776544edf4bc3daa9e471 (diff)
downloadillumos-joyent-OS-7870.tar.gz
Merge branch 'master' into OS-7870OS-7870
-rw-r--r--manifest14
-rw-r--r--usr/src/boot/Makefile.version2
-rw-r--r--usr/src/boot/include/stdlib.h3
-rw-r--r--usr/src/boot/lib/libstand/zfs/zfsimpl.c660
-rw-r--r--usr/src/boot/sys/boot/forth/loader.conf2
-rw-r--r--usr/src/boot/sys/boot/i386/libi386/biosdisk.c4
-rw-r--r--usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h8
-rw-r--r--usr/src/boot/sys/cddl/boot/zfs/zfssubr.c6
-rw-r--r--usr/src/cmd/Makefile2
-rw-r--r--usr/src/cmd/addbadsec/Makefile4
-rw-r--r--usr/src/cmd/audio/audioconvert/Makefile1
-rw-r--r--usr/src/cmd/audio/include/AudioError.h3
-rw-r--r--usr/src/cmd/audio/include/AudioTypes.h1
-rw-r--r--usr/src/cmd/audio/utilities/Makefile1
-rw-r--r--usr/src/cmd/auditreduce/Makefile2
-rw-r--r--usr/src/cmd/bart/Makefile2
-rw-r--r--usr/src/cmd/ccidadm/Makefile51
-rw-r--r--usr/src/cmd/ccidadm/ccidadm.c844
-rw-r--r--usr/src/cmd/devfsadm/Makefile.com2
-rw-r--r--usr/src/cmd/devfsadm/cfg_link.c29
-rw-r--r--usr/src/cmd/devfsadm/cfg_link.h4
-rw-r--r--usr/src/cmd/devfsadm/usb_link.c36
-rw-r--r--usr/src/cmd/dumpadm/svc-dumpadm14
-rw-r--r--usr/src/cmd/fs.d/nfs/nfsd/Makefile2
-rw-r--r--usr/src/cmd/genmsg/Makefile2
-rw-r--r--usr/src/cmd/init/init.c5
-rw-r--r--usr/src/cmd/ksh/builtins/Makefile1
-rw-r--r--usr/src/cmd/make/Makefile.com1
-rw-r--r--usr/src/cmd/make/include/mksh/defs.h1
-rw-r--r--usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c18
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c2
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kproc.c40
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kvm.c2
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_main.c6
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_proc.c12
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_rawfile.c54
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_value.c66
-rw-r--r--usr/src/cmd/mdb/common/modules/crypto/impl.c2
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/memory.c2
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/tsd.c3
-rw-r--r--usr/src/cmd/mdb/common/modules/ufs/ufs.c3
-rw-r--r--usr/src/cmd/mdb/common/modules/ufs/ufs_log.c3
-rw-r--r--usr/src/cmd/mdb/common/modules/usba/prtusb.c35
-rw-r--r--usr/src/cmd/mdb/intel/mdb/kvm_amd64dep.c34
-rw-r--r--usr/src/cmd/mdb/intel/mdb/kvm_ia32dep.c20
-rw-r--r--usr/src/cmd/picl/plugins/common/devtree/picldevtree.c70
-rw-r--r--usr/src/cmd/power/Makefile14
-rw-r--r--usr/src/cmd/praudit/Makefile2
-rw-r--r--usr/src/cmd/sleep/Makefile29
-rw-r--r--usr/src/cmd/sleep/sleep.c168
-rw-r--r--usr/src/cmd/smbios/Makefile2
-rw-r--r--usr/src/cmd/smbios/smbios.c145
-rw-r--r--usr/src/cmd/sort/common/initialize.c18
-rw-r--r--usr/src/cmd/truss/print.c11
-rw-r--r--usr/src/cmd/vgrind/Makefile16
-rw-r--r--usr/src/common/ccid/atr.c1604
-rw-r--r--usr/src/common/ccid/atr.h198
-rw-r--r--usr/src/common/smbios/mktables.sh8
-rw-r--r--usr/src/common/smbios/smb_info.c143
-rw-r--r--usr/src/common/smbios/smb_open.c1
-rw-r--r--usr/src/grub/grub-0.97/Makefile.solaris.defs8
-rw-r--r--usr/src/lib/Makefile2
-rw-r--r--usr/src/lib/cfgadm_plugins/Makefile5
-rw-r--r--usr/src/lib/cfgadm_plugins/ccid/Makefile67
-rw-r--r--usr/src/lib/cfgadm_plugins/ccid/Makefile.com67
-rw-r--r--usr/src/lib/cfgadm_plugins/ccid/amd64/Makefile21
-rw-r--r--usr/src/lib/cfgadm_plugins/ccid/common/cfga_ccid.c421
-rw-r--r--usr/src/lib/cfgadm_plugins/ccid/common/mapfile-vers42
-rw-r--r--usr/src/lib/cfgadm_plugins/ccid/i386/Makefile20
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c8
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/mapfile-vers1
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.c2
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.h1
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.map1
-rw-r--r--usr/src/lib/fm/topo/modules/common/ses/ses.c183
-rw-r--r--usr/src/lib/libbe/common/be_create.c13
-rw-r--r--usr/src/lib/libbe/common/be_rename.c13
-rw-r--r--usr/src/lib/libbe/common/be_utils.c31
-rw-r--r--usr/src/lib/libbe/common/libbe_priv.h3
-rw-r--r--usr/src/lib/libc/i386/Makefile.com4
-rw-r--r--usr/src/lib/libcmdutils/common/nicenum.c14
-rw-r--r--usr/src/lib/libcmdutils/libcmdutils.h3
-rw-r--r--usr/src/lib/libdtrace/common/dt_options.c13
-rw-r--r--usr/src/lib/libmail/common/notifyu.c12
-rw-r--r--usr/src/lib/libpcsc/Makefile43
-rw-r--r--usr/src/lib/libpcsc/Makefile.com32
-rw-r--r--usr/src/lib/libpcsc/amd64/Makefile19
-rw-r--r--usr/src/lib/libpcsc/common/libpcsc.c615
-rw-r--r--usr/src/lib/libpcsc/common/mapfile-vers50
-rw-r--r--usr/src/lib/libpcsc/common/winscard.h144
-rw-r--r--usr/src/lib/libpcsc/common/wintypes.h50
-rw-r--r--usr/src/lib/libpcsc/i386/Makefile18
-rw-r--r--usr/src/lib/libsasl/lib/common.c2
-rw-r--r--usr/src/lib/libshell/amd64/src/cmd/ksh93/FEATURE/math34
-rw-r--r--usr/src/lib/libshell/common/sh/streval.c4
-rw-r--r--usr/src/lib/libshell/i386/src/cmd/ksh93/FEATURE/math34
-rw-r--r--usr/src/lib/libsmbios/common/mapfile-vers11
-rw-r--r--usr/src/lib/smbclnt/libfksmbfs/common/fake_vfs.c5
-rw-r--r--usr/src/lib/smbclnt/libfksmbfs/common/fake_vnode.c8
-rw-r--r--usr/src/man/man1/sleep.1305
-rw-r--r--usr/src/man/man1m/Makefile1
-rw-r--r--usr/src/man/man1m/ccidadm.1m120
-rw-r--r--usr/src/man/man7d/Makefile1
-rw-r--r--usr/src/man/man7d/audio.7d44
-rw-r--r--usr/src/man/man7d/av1394.7d35
-rw-r--r--usr/src/man/man7d/bnxe.7d20
-rw-r--r--usr/src/man/man7d/ccid.7d460
-rw-r--r--usr/src/man/man7d/coretemp.7d4
-rw-r--r--usr/src/man/man7d/dcam1394.7d35
-rw-r--r--usr/src/man/man7d/ecpp.7d58
-rw-r--r--usr/src/man/man7d/ehci.7d24
-rw-r--r--usr/src/man/man7d/elxl.7d4
-rw-r--r--usr/src/man/man7d/fcoe.7d29
-rw-r--r--usr/src/man/man7d/fcoei.7d27
-rw-r--r--usr/src/man/man7d/fcoet.7d28
-rw-r--r--usr/src/man/man7d/hci1394.7d26
-rw-r--r--usr/src/man/man7d/i40e.7d20
-rw-r--r--usr/src/man/man7d/ieee1394.7d36
-rw-r--r--usr/src/man/man7d/ixgbe.7d25
-rw-r--r--usr/src/man/man7d/ntwdt.7d22
-rw-r--r--usr/src/man/man7d/pchtemp.7d4
-rw-r--r--usr/src/man/man7d/pcn.7d72
-rw-r--r--usr/src/man/man7d/poll.7d10
-rw-r--r--usr/src/man/man7d/qede.7d4
-rw-r--r--usr/src/man/man7d/sd.7d21
-rw-r--r--usr/src/man/man7d/usba.7d61
-rw-r--r--usr/src/man/man7d/xhci.7d10
-rw-r--r--usr/src/man/man7i/Makefile6
-rw-r--r--usr/src/pkg/manifests/SUNWcs.mf2
-rw-r--r--usr/src/pkg/manifests/system-test-utiltest.mf5
-rw-r--r--usr/src/prototypes/README2
-rw-r--r--usr/src/prototypes/prototype.Makefile2
-rw-r--r--usr/src/prototypes/prototype.c2
-rw-r--r--usr/src/prototypes/prototype.csh2
-rw-r--r--usr/src/prototypes/prototype.h2
-rw-r--r--usr/src/prototypes/prototype.java2
-rw-r--r--usr/src/prototypes/prototype.ksh2
-rw-r--r--usr/src/prototypes/prototype.man2
-rw-r--r--usr/src/prototypes/prototype.mapfile-vers2
-rw-r--r--usr/src/prototypes/prototype.pl2
-rw-r--r--usr/src/prototypes/prototype.py2
-rw-r--r--usr/src/prototypes/prototype.s2
-rw-r--r--usr/src/prototypes/prototype.sh2
-rw-r--r--usr/src/test/os-tests/runfiles/default.run8
-rw-r--r--usr/src/test/os-tests/tests/Makefile1
-rw-r--r--usr/src/test/os-tests/tests/uccid/Makefile82
-rw-r--r--usr/src/test/os-tests/tests/uccid/atrparse.c731
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-badread.c80
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-basic.c65
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-close.c81
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-loop.c84
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-nonblock.c99
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-reset.c65
-rw-r--r--usr/src/test/os-tests/tests/uccid/modify.c181
-rw-r--r--usr/src/test/os-tests/tests/uccid/notxn-poll.c57
-rw-r--r--usr/src/test/os-tests/tests/uccid/pollin.c64
-rw-r--r--usr/src/test/os-tests/tests/uccid/pollout.c68
-rw-r--r--usr/src/test/os-tests/tests/uccid/status.c97
-rw-r--r--usr/src/test/os-tests/tests/uccid/txn-pollerr.c88
-rw-r--r--usr/src/test/os-tests/tests/uccid/yk-poll.c108
-rw-r--r--usr/src/test/os-tests/tests/uccid/yk-readonly.c84
-rw-r--r--usr/src/test/os-tests/tests/uccid/yk.c78
-rw-r--r--usr/src/test/util-tests/cmd/utiltest.ksh2
-rw-r--r--usr/src/test/util-tests/runfiles/default.run7
-rw-r--r--usr/src/test/util-tests/tests/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/sleep/Makefile47
-rw-r--r--usr/src/test/util-tests/tests/sleep/sleep.awk22
-rw-r--r--usr/src/test/util-tests/tests/sleep/sleep.d40
-rw-r--r--usr/src/test/util-tests/tests/sleep/sleeptest.ksh180
-rw-r--r--usr/src/test/util-tests/tests/smbios/Makefile20
-rw-r--r--usr/src/test/util-tests/tests/smbios/smbios.c376
-rw-r--r--usr/src/test/util-tests/tests/smbios/smbios_test.h110
-rw-r--r--usr/src/test/util-tests/tests/smbios/smbios_test_errors.c28
-rw-r--r--usr/src/test/util-tests/tests/smbios/smbios_test_memdevice.c355
-rw-r--r--usr/src/test/util-tests/tests/smbios/smbios_test_pinfo.c585
-rw-r--r--usr/src/test/util-tests/tests/smbios/smbios_test_slot.c188
-rw-r--r--usr/src/tools/make/Makefile.com2
-rw-r--r--usr/src/tools/scripts/git-pbchk.py4
-rw-r--r--usr/src/tools/smatch/Makefile8
-rw-r--r--usr/src/tools/smatch/src/Makefile5
-rw-r--r--usr/src/tools/smatch/src/check_debug.c16
-rw-r--r--usr/src/tools/smatch/src/check_debug.h3
-rw-r--r--usr/src/tools/smatch/src/check_frees_param.c17
-rw-r--r--usr/src/tools/smatch/src/check_list.h8
-rw-r--r--usr/src/tools/smatch/src/check_locking.c1704
-rw-r--r--usr/src/tools/smatch/src/check_param_mapper.c69
-rw-r--r--usr/src/tools/smatch/src/smatch.c3
-rw-r--r--usr/src/tools/smatch/src/smatch.h91
-rw-r--r--usr/src/tools/smatch/src/smatch_address.c56
-rw-r--r--usr/src/tools/smatch/src/smatch_buf_size.c1
-rw-r--r--usr/src/tools/smatch/src/smatch_comparison.c147
-rw-r--r--usr/src/tools/smatch/src/smatch_container_of.c325
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/create_db.sh1
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh5
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/insert_manual_states.pl93
-rw-r--r--usr/src/tools/smatch/src/smatch_data/db/kernel.insert.return_states12
-rw-r--r--usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions1
-rw-r--r--usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions2
-rw-r--r--usr/src/tools/smatch/src/smatch_db.c227
-rw-r--r--usr/src/tools/smatch/src/smatch_expressions.c13
-rw-r--r--usr/src/tools/smatch/src/smatch_extra.c29
-rw-r--r--usr/src/tools/smatch/src/smatch_extra.h1
-rw-r--r--usr/src/tools/smatch/src/smatch_function_hooks.c20
-rw-r--r--usr/src/tools/smatch/src/smatch_helper.c27
-rw-r--r--usr/src/tools/smatch/src/smatch_implied.c58
-rw-r--r--usr/src/tools/smatch/src/smatch_math.c3
-rw-r--r--usr/src/tools/smatch/src/smatch_parsed_conditions.c121
-rw-r--r--usr/src/tools/smatch/src/smatch_ranges.c12
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh2
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh2
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh1
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/kpatch.sh7
-rw-r--r--usr/src/tools/smatch/src/smatch_states.c9
-rw-r--r--usr/src/tools/smatch/src/smatch_sval.c169
-rw-r--r--usr/src/tools/smatch/src/smatch_type.c72
-rw-r--r--usr/src/tools/smatch/src/validation/sm_float1.c20
-rw-r--r--usr/src/ucblib/libcurses/i386/Makefile2
-rw-r--r--usr/src/ucblib/libdbm/i386/Makefile2
-rw-r--r--usr/src/ucblib/librpcsoc/i386/Makefile2
-rw-r--r--usr/src/ucblib/libtermcap/i386/Makefile2
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/Makefile.rules8
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_link.c1
-rw-r--r--usr/src/uts/common/fs/zfs/btree.c8
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_scan.c19
-rw-r--r--usr/src/uts/common/fs/zfs/range_tree.c8
-rw-r--r--usr/src/uts/common/fs/zfs/sys/btree.h21
-rw-r--r--usr/src/uts/common/fs/zfs/sys/vdev_disk.h67
-rw-r--r--usr/src/uts/common/fs/zfs/sys/vdev_impl.h3
-rw-r--r--usr/src/uts/common/fs/zfs/sys/vdev_raidz.h48
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_disk.c75
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_file.c2
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_indirect.c2
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_mirror.c33
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_missing.c3
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_raidz.c24
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_root.c2
-rw-r--r--usr/src/uts/common/fs/zfs/zvol.c52
-rw-r--r--usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c18
-rw-r--r--usr/src/uts/common/io/usb/clients/ccid/ccid.c4389
-rw-r--r--usr/src/uts/common/os/logsubr.c4
-rw-r--r--usr/src/uts/common/sys/Makefile7
-rw-r--r--usr/src/uts/common/sys/Makefile.syshdrs6
-rw-r--r--usr/src/uts/common/sys/smbios.h158
-rw-r--r--usr/src/uts/common/sys/smbios_impl.h40
-rw-r--r--usr/src/uts/common/sys/sunddi.h2
-rw-r--r--usr/src/uts/common/sys/usb/clients/ccid/ccid.h311
-rw-r--r--usr/src/uts/common/sys/usb/clients/ccid/uccid.h133
-rw-r--r--usr/src/uts/intel/Makefile.intel1
-rw-r--r--usr/src/uts/intel/ccid/Makefile42
-rw-r--r--usr/src/uts/intel/os/driver_aliases1
-rw-r--r--usr/src/uts/intel/os/name_to_major1
252 files changed, 18126 insertions, 2829 deletions
diff --git a/manifest b/manifest
index 74a5b345c8..9dec860247 100644
--- a/manifest
+++ b/manifest
@@ -634,6 +634,7 @@ f kernel/drv/amd64/blkdev 0755 root sys
f kernel/drv/amd64/bnx 0755 root sys
f kernel/drv/amd64/bnxe 0755 root sys
f kernel/drv/amd64/bridge 0755 root sys
+f kernel/drv/amd64/ccid 0755 root sys
f kernel/drv/amd64/chxge 0755 root sys
f kernel/drv/amd64/clone 0755 root sys
f kernel/drv/amd64/cmdk 0755 root sys
@@ -2801,7 +2802,7 @@ h usr/bin/setuname=usr/lib/isaexec
s usr/bin/sh=ksh93
f usr/bin/shcomp 0555 root bin
f usr/bin/size 0555 root bin
-h usr/bin/sleep=usr/bin/alias
+f usr/bin/sleep 0555 root bin
f usr/bin/smbutil 0555 root bin
f usr/bin/soelim 0555 root bin
h usr/bin/sort=usr/lib/isaexec
@@ -5281,6 +5282,7 @@ s usr/lib/amd64/libpam.so=../../../lib/amd64/libpam.so.1
f usr/lib/amd64/libpanel.so.1 0755 root bin
s usr/lib/amd64/libpanel.so=libpanel.so.1
f usr/lib/amd64/libpcidb.so.1 0755 root bin
+f usr/lib/amd64/libpcsc.so.1 0755 root bin
f usr/lib/amd64/libpctx.so.1 0755 root bin
s usr/lib/amd64/libpctx.so=libpctx.so.1
f usr/lib/amd64/libpicl.so.1 0755 root bin
@@ -5550,8 +5552,12 @@ f usr/lib/brand/shared/common.ksh 0444 root bin
f usr/lib/brand/shared/query 0755 root bin
f usr/lib/brand/shared/uninstall.ksh 0444 root bin
f usr/lib/bridged 0555 root bin
+d usr/lib/ccid 0755 root bin
+f usr/lib/ccid/ccidadm 0555 root bin
d usr/lib/cfgadm 0755 root bin
d usr/lib/cfgadm/amd64 0755 root bin
+f usr/lib/cfgadm/amd64/ccid.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/ccid.so=ccid.so.1
f usr/lib/cfgadm/amd64/fp.so.1 0755 root bin
s usr/lib/cfgadm/amd64/fp.so=fp.so.1
f usr/lib/cfgadm/amd64/ib.so.1 0755 root bin
@@ -5566,6 +5572,8 @@ f usr/lib/cfgadm/amd64/shp.so.1 0755 root bin
s usr/lib/cfgadm/amd64/shp.so=shp.so.1
f usr/lib/cfgadm/amd64/usb.so.1 0755 root bin
s usr/lib/cfgadm/amd64/usb.so=usb.so.1
+f usr/lib/cfgadm/ccid.so.1 0755 root bin
+s usr/lib/cfgadm/ccid.so=ccid.so.1
f usr/lib/cfgadm/fp.so.1 0755 root bin
s usr/lib/cfgadm/fp.so=fp.so.1
f usr/lib/cfgadm/ib.so.1 0755 root bin
@@ -6786,6 +6794,7 @@ s usr/lib/libpam.so=../../lib/libpam.so.1
f usr/lib/libpanel.so.1 0755 root bin
s usr/lib/libpanel.so=libpanel.so.1
f usr/lib/libpcidb.so.1 0755 root bin
+f usr/lib/libpcsc.so.1 0755 root bin
f usr/lib/libpctx.so.1 0755 root bin
s usr/lib/libpctx.so=libpctx.so.1
f usr/lib/libpicl.so.1 0755 root bin
@@ -12297,6 +12306,7 @@ f usr/share/man/man1m/bootadm.1m 0444 root bin
f usr/share/man/man1m/busstat.1m 0444 root bin
f usr/share/man/man1m/captoinfo.1m 0444 root bin
f usr/share/man/man1m/catman.1m 0444 root bin
+f usr/share/man/man1m/ccidadm.1m 0444 root bin
f usr/share/man/man1m/cfgadm.1m 0444 root bin
f usr/share/man/man1m/cfgadm_fp.1m 0444 root bin
f usr/share/man/man1m/cfgadm_pci.1m 0444 root bin
@@ -19459,6 +19469,7 @@ f usr/share/man/man7d/bfe.7d 0444 root bin
f usr/share/man/man7d/bge.7d 0444 root bin
f usr/share/man/man7d/blkdev.7d 0444 root bin
f usr/share/man/man7d/bnxe.7d 0444 root bin
+f usr/share/man/man7d/ccid.7d 0444 root bin
f usr/share/man/man7d/chxge.7d 0444 root bin
f usr/share/man/man7d/cmdk.7d 0444 root bin
f usr/share/man/man7d/coretemp.7d 0444 root bin
@@ -19621,6 +19632,7 @@ f usr/share/man/man7i/sockio.7i 0444 root bin
f usr/share/man/man7i/streamio.7i 0444 root bin
f usr/share/man/man7i/termio.7i 0444 root bin
f usr/share/man/man7i/termiox.7i 0444 root bin
+s usr/share/man/man7i/uccid.7i=../man7d/ccid.7d
f usr/share/man/man7i/uscsi.7i 0444 root bin
f usr/share/man/man7i/visual_io.7i 0444 root bin
f usr/share/man/man7i/vt.7i 0444 root bin
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version
index 707e31612b..95c2115b2b 100644
--- a/usr/src/boot/Makefile.version
+++ b/usr/src/boot/Makefile.version
@@ -33,4 +33,4 @@ LOADER_VERSION = 1.1
# Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes.
# The version is processed from left to right, the version number can only
# be increased.
-BOOT_VERSION = $(LOADER_VERSION)-2019.12.18.2
+BOOT_VERSION = $(LOADER_VERSION)-2020.01.09.1
diff --git a/usr/src/boot/include/stdlib.h b/usr/src/boot/include/stdlib.h
index d5506ff65e..b8708a27a5 100644
--- a/usr/src/boot/include/stdlib.h
+++ b/usr/src/boot/include/stdlib.h
@@ -172,8 +172,7 @@ char *realpath(const char * __restrict, char * __restrict);
int rand_r(unsigned *); /* (TSF) */
#endif
#if __POSIX_VISIBLE >= 200112
-int posix_memalign(void **, size_t, size_t) __nonnull(1) __alloc_align(2)
- __alloc_size(3); /* (ADV) */
+int posix_memalign(void **, size_t, size_t); /* (ADV) */
int setenv(const char *, const char *, int);
int unsetenv(const char *);
#endif
diff --git a/usr/src/boot/lib/libstand/zfs/zfsimpl.c b/usr/src/boot/lib/libstand/zfs/zfsimpl.c
index a85f6f5721..212b5faa52 100644
--- a/usr/src/boot/lib/libstand/zfs/zfsimpl.c
+++ b/usr/src/boot/lib/libstand/zfs/zfsimpl.c
@@ -490,12 +490,12 @@ vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf,
}
rc = vdev->v_phys_read(vdev, vdev->v_read_priv, offset, buf, psize);
- if (rc)
- return (rc);
- if (bp != NULL)
- return (zio_checksum_verify(vdev->spa, bp, buf));
+ if (rc == 0) {
+ if (bp != NULL)
+ rc = zio_checksum_verify(vdev->v_spa, bp, buf);
+ }
- return (0);
+ return (rc);
}
typedef struct remap_segment {
@@ -774,8 +774,10 @@ static vdev_t *
vdev_lookup_top(spa_t *spa, uint64_t vdev)
{
vdev_t *rvd;
+ vdev_list_t *vlist;
- STAILQ_FOREACH(rvd, &spa->spa_vdevs, v_childlink)
+ vlist = &spa->spa_root_vdev->v_children;
+ STAILQ_FOREACH(rvd, vlist, v_childlink)
if (rvd->v_id == vdev)
break;
@@ -838,7 +840,7 @@ static void
vdev_indirect_remap(vdev_t *vd, uint64_t offset, uint64_t asize, void *arg)
{
list_t stack;
- spa_t *spa = vd->spa;
+ spa_t *spa = vd->v_spa;
zio_t *zio = arg;
remap_segment_t *rs;
@@ -931,19 +933,20 @@ static int
vdev_indirect_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
off_t offset, size_t bytes)
{
- zio_t zio = { 0 };
- spa_t *spa = vdev->spa;
- indirect_vsd_t *iv = malloc(sizeof (*iv));
+ zio_t zio;
+ spa_t *spa = vdev->v_spa;
+ indirect_vsd_t *iv;
indirect_split_t *first;
int rc = EIO;
+ iv = calloc(1, sizeof (*iv));
if (iv == NULL)
return (ENOMEM);
- bzero(iv, sizeof (*iv));
list_create(&iv->iv_splits,
sizeof (indirect_split_t), offsetof(indirect_split_t, is_node));
+ bzero(&zio, sizeof (zio));
zio.io_spa = spa;
zio.io_bp = (blkptr_t *)bp;
zio.io_data = buf;
@@ -1082,40 +1085,73 @@ vdev_create(uint64_t guid, vdev_read_t *vdev_read)
vdev_t *vdev;
vdev_indirect_config_t *vic;
- vdev = malloc(sizeof (vdev_t));
- memset(vdev, 0, sizeof (vdev_t));
- STAILQ_INIT(&vdev->v_children);
- vdev->v_guid = guid;
- vdev->v_state = VDEV_STATE_OFFLINE;
- vdev->v_read = vdev_read;
+ vdev = calloc(1, sizeof (vdev_t));
+ if (vdev != NULL) {
+ STAILQ_INIT(&vdev->v_children);
+ vdev->v_guid = guid;
+ vdev->v_read = vdev_read;
- vic = &vdev->vdev_indirect_config;
- vic->vic_prev_indirect_vdev = UINT64_MAX;
- STAILQ_INSERT_TAIL(&zfs_vdevs, vdev, v_alllink);
+ /*
+ * root vdev has no read function, we use this fact to
+ * skip setting up data we do not need for root vdev.
+ * We only point root vdev from spa.
+ */
+ if (vdev_read != NULL) {
+ vic = &vdev->vdev_indirect_config;
+ vic->vic_prev_indirect_vdev = UINT64_MAX;
+ STAILQ_INSERT_TAIL(&zfs_vdevs, vdev, v_alllink);
+ }
+ }
return (vdev);
}
-static int
-vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev,
- vdev_t **vdevp, int is_newer)
+static void
+vdev_set_initial_state(vdev_t *vdev, const unsigned char *nvlist)
{
- int rc;
- uint64_t guid, id, ashift, asize, nparity;
- const char *type;
- const char *path;
- vdev_t *vdev, *kid;
- const unsigned char *kids;
- int nkids, i, is_new;
uint64_t is_offline, is_faulted, is_degraded, is_removed, isnt_present;
uint64_t is_log;
- if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
- NULL, &guid) ||
- nvlist_find(nvlist, ZPOOL_CONFIG_ID, DATA_TYPE_UINT64, NULL, &id) ||
+ is_offline = is_removed = is_faulted = is_degraded = isnt_present = 0;
+ is_log = 0;
+ (void) nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, NULL,
+ &is_offline);
+ (void) nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, NULL,
+ &is_removed);
+ (void) nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, NULL,
+ &is_faulted);
+ (void) nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64,
+ NULL, &is_degraded);
+ (void) nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64,
+ NULL, &isnt_present);
+ (void) nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64, NULL,
+ &is_log);
+
+ if (is_offline != 0)
+ vdev->v_state = VDEV_STATE_OFFLINE;
+ else if (is_removed != 0)
+ vdev->v_state = VDEV_STATE_REMOVED;
+ else if (is_faulted != 0)
+ vdev->v_state = VDEV_STATE_FAULTED;
+ else if (is_degraded != 0)
+ vdev->v_state = VDEV_STATE_DEGRADED;
+ else if (isnt_present != 0)
+ vdev->v_state = VDEV_STATE_CANT_OPEN;
+
+ vdev->v_islog = is_log != 0;
+}
+
+static int
+vdev_init(uint64_t guid, const unsigned char *nvlist, vdev_t **vdevp)
+{
+ uint64_t id, ashift, asize, nparity;
+ const char *path;
+ const char *type;
+ vdev_t *vdev;
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_ID, DATA_TYPE_UINT64, NULL, &id) ||
nvlist_find(nvlist, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING,
NULL, &type)) {
- printf("ZFS: can't find vdev details\n");
return (ENOENT);
}
@@ -1132,153 +1168,238 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev,
return (EIO);
}
- is_offline = is_removed = is_faulted = is_degraded = isnt_present = 0;
- is_log = 0;
-
- nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, NULL,
- &is_offline);
- nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, NULL,
- &is_removed);
- nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, NULL,
- &is_faulted);
- nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64, NULL,
- &is_degraded);
- nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64, NULL,
- &isnt_present);
- nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64, NULL,
- &is_log);
+ if (strcmp(type, VDEV_TYPE_MIRROR) == 0)
+ vdev = vdev_create(guid, vdev_mirror_read);
+ else if (strcmp(type, VDEV_TYPE_RAIDZ) == 0)
+ vdev = vdev_create(guid, vdev_raidz_read);
+ else if (strcmp(type, VDEV_TYPE_REPLACING) == 0)
+ vdev = vdev_create(guid, vdev_replacing_read);
+ else if (strcmp(type, VDEV_TYPE_INDIRECT) == 0) {
+ vdev_indirect_config_t *vic;
- vdev = vdev_find(guid);
- if (!vdev) {
- is_new = 1;
-
- if (strcmp(type, VDEV_TYPE_MIRROR) == 0)
- vdev = vdev_create(guid, vdev_mirror_read);
- else if (strcmp(type, VDEV_TYPE_RAIDZ) == 0)
- vdev = vdev_create(guid, vdev_raidz_read);
- else if (strcmp(type, VDEV_TYPE_REPLACING) == 0)
- vdev = vdev_create(guid, vdev_replacing_read);
- else if (strcmp(type, VDEV_TYPE_INDIRECT) == 0) {
- vdev_indirect_config_t *vic;
-
- vdev = vdev_create(guid, vdev_indirect_read);
+ vdev = vdev_create(guid, vdev_indirect_read);
+ if (vdev != NULL) {
vdev->v_state = VDEV_STATE_HEALTHY;
vic = &vdev->vdev_indirect_config;
nvlist_find(nvlist,
- ZPOOL_CONFIG_INDIRECT_OBJECT, DATA_TYPE_UINT64,
+ ZPOOL_CONFIG_INDIRECT_OBJECT,
+ DATA_TYPE_UINT64,
NULL, &vic->vic_mapping_object);
nvlist_find(nvlist,
- ZPOOL_CONFIG_INDIRECT_BIRTHS, DATA_TYPE_UINT64,
+ ZPOOL_CONFIG_INDIRECT_BIRTHS,
+ DATA_TYPE_UINT64,
NULL, &vic->vic_births_object);
nvlist_find(nvlist,
- ZPOOL_CONFIG_PREV_INDIRECT_VDEV, DATA_TYPE_UINT64,
+ ZPOOL_CONFIG_PREV_INDIRECT_VDEV,
+ DATA_TYPE_UINT64,
NULL, &vic->vic_prev_indirect_vdev);
- } else
- vdev = vdev_create(guid, vdev_disk_read);
-
- vdev->v_id = id;
- vdev->v_top = pvdev != NULL ? pvdev : vdev;
- if (nvlist_find(nvlist, ZPOOL_CONFIG_ASHIFT,
- DATA_TYPE_UINT64, NULL, &ashift) == 0) {
- vdev->v_ashift = ashift;
- } else {
- vdev->v_ashift = 0;
}
- if (nvlist_find(nvlist, ZPOOL_CONFIG_ASIZE,
- DATA_TYPE_UINT64, NULL, &asize) == 0) {
- vdev->v_psize = asize +
- VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
- }
- if (nvlist_find(nvlist, ZPOOL_CONFIG_NPARITY,
- DATA_TYPE_UINT64, NULL, &nparity) == 0) {
- vdev->v_nparity = nparity;
+ } else {
+ vdev = vdev_create(guid, vdev_disk_read);
+ }
+
+ if (vdev == NULL)
+ return (ENOMEM);
+
+ vdev_set_initial_state(vdev, nvlist);
+ vdev->v_id = id;
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_ASHIFT,
+ DATA_TYPE_UINT64, NULL, &ashift) == 0)
+ vdev->v_ashift = ashift;
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_ASIZE,
+ DATA_TYPE_UINT64, NULL, &asize) == 0) {
+ vdev->v_psize = asize +
+ VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
+ }
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_NPARITY,
+ DATA_TYPE_UINT64, NULL, &nparity) == 0)
+ vdev->v_nparity = nparity;
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH,
+ DATA_TYPE_STRING, NULL, &path) == 0) {
+ if (strncmp(path, "/dev/dsk/", 9) == 0)
+ path += 9;
+ vdev->v_name = strdup(path);
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_PHYS_PATH,
+ DATA_TYPE_STRING, NULL, &path) == 0) {
+ vdev->v_phys_path = strdup(path);
} else {
- vdev->v_nparity = 0;
+ vdev->v_phys_path = NULL;
}
- if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH,
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_DEVID,
DATA_TYPE_STRING, NULL, &path) == 0) {
- if (strncmp(path, "/dev/dsk/", 9) == 0)
- path += 9;
- vdev->v_name = strdup(path);
- if (nvlist_find(nvlist, ZPOOL_CONFIG_PHYS_PATH,
- DATA_TYPE_STRING, NULL, &path) == 0) {
- vdev->v_phys_path = strdup(path);
- } else {
- vdev->v_phys_path = NULL;
- }
- if (nvlist_find(nvlist, ZPOOL_CONFIG_DEVID,
- DATA_TYPE_STRING, NULL, &path) == 0) {
- vdev->v_devid = strdup(path);
- } else {
- vdev->v_devid = NULL;
- }
+ vdev->v_devid = strdup(path);
} else {
- char *name;
-
- if (strcmp(type, "raidz") == 0) {
- if (vdev->v_nparity < 1 ||
- vdev->v_nparity > 3) {
- printf("ZFS: can only boot from disk, "
- "mirror, raidz1, raidz2 and raidz3 "
- "vdevs\n");
- return (EIO);
- }
- rc = asprintf(&name, "%s%d-%" PRIu64, type,
- vdev->v_nparity, id);
- } else {
- rc = asprintf(&name, "%s-%" PRIu64, type, id);
- }
- if (rc < 0)
- return (ENOMEM);
- vdev->v_name = name;
+ vdev->v_devid = NULL;
}
- vdev->v_islog = is_log == 1;
} else {
- is_new = 0;
+ char *name;
+
+ name = NULL;
+ if (strcmp(type, "raidz") == 0) {
+ if (vdev->v_nparity < 1 ||
+ vdev->v_nparity > 3) {
+ printf("ZFS: invalid raidz parity: %d\n",
+ vdev->v_nparity);
+ return (EIO);
+ }
+ (void) asprintf(&name, "%s%d-%" PRIu64, type,
+ vdev->v_nparity, id);
+ } else {
+ (void) asprintf(&name, "%s-%" PRIu64, type, id);
+ }
+ vdev->v_name = name;
}
+ *vdevp = vdev;
+ return (0);
+}
+
+/*
+ * Find slot for vdev. We return either NULL to signal to use
+ * STAILQ_INSERT_HEAD, or we return link element to be used with
+ * STAILQ_INSERT_AFTER.
+ */
+static vdev_t *
+vdev_find_previous(vdev_t *top_vdev, vdev_t *vdev)
+{
+ vdev_t *v, *previous;
+
+ if (STAILQ_EMPTY(&top_vdev->v_children))
+ return (NULL);
+
+ previous = NULL;
+ STAILQ_FOREACH(v, &top_vdev->v_children, v_childlink) {
+ if (v->v_id > vdev->v_id)
+ return (previous);
+
+ if (v->v_id == vdev->v_id)
+ return (v);
+
+ if (v->v_id < vdev->v_id)
+ previous = v;
+ }
+ return (previous);
+}
+
+static size_t
+vdev_child_count(vdev_t *vdev)
+{
+ vdev_t *v;
+ size_t count;
+
+ count = 0;
+ STAILQ_FOREACH(v, &vdev->v_children, v_childlink) {
+ count++;
+ }
+ return (count);
+}
+
+/*
+ * Insert vdev into top_vdev children list. List is ordered by v_id.
+ */
+static void
+vdev_insert(vdev_t *top_vdev, vdev_t *vdev)
+{
+ vdev_t *previous;
+ size_t count;
- if (is_new || is_newer) {
+ /*
+ * The top level vdev can appear in random order, depending how
+ * the firmware is presenting the disk devices.
+ * However, we will insert vdev to create list ordered by v_id,
+ * so we can use either STAILQ_INSERT_HEAD or STAILQ_INSERT_AFTER
+ * as STAILQ does not have insert before.
+ */
+ previous = vdev_find_previous(top_vdev, vdev);
+
+ if (previous == NULL) {
+ STAILQ_INSERT_HEAD(&top_vdev->v_children, vdev, v_childlink);
+ } else if (previous->v_id == vdev->v_id) {
/*
- * This is either new vdev or we've already seen this vdev,
- * but from an older vdev label, so let's refresh its state
- * from the newer label.
+ * This vdev was configured from label config,
+ * do not insert duplicate.
*/
- if (is_offline)
- vdev->v_state = VDEV_STATE_OFFLINE;
- else if (is_removed)
- vdev->v_state = VDEV_STATE_REMOVED;
- else if (is_faulted)
- vdev->v_state = VDEV_STATE_FAULTED;
- else if (is_degraded)
- vdev->v_state = VDEV_STATE_DEGRADED;
- else if (isnt_present)
- vdev->v_state = VDEV_STATE_CANT_OPEN;
+ return;
+ } else {
+ STAILQ_INSERT_AFTER(&top_vdev->v_children, previous, vdev,
+ v_childlink);
+ }
+
+ count = vdev_child_count(top_vdev);
+ if (top_vdev->v_nchildren < count)
+ top_vdev->v_nchildren = count;
+}
+
+static int
+vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const unsigned char *nvlist)
+{
+ vdev_t *top_vdev, *vdev;
+ const unsigned char *kids;
+ int rc, nkids;
+
+ /* Get top vdev. */
+ top_vdev = vdev_find(top_guid);
+ if (top_vdev == NULL) {
+ rc = vdev_init(top_guid, nvlist, &top_vdev);
+ if (rc != 0)
+ return (rc);
+ top_vdev->v_spa = spa;
+ top_vdev->v_top = top_vdev;
+ vdev_insert(spa->spa_root_vdev, top_vdev);
}
+ /* Add children if there are any. */
rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
&nkids, &kids);
- /*
- * Its ok if we don't have any kids.
- */
if (rc == 0) {
- vdev->v_nchildren = nkids;
- for (i = 0; i < nkids; i++) {
- rc = vdev_init_from_nvlist(kids, vdev, &kid, is_newer);
- if (rc)
+ for (int i = 0; i < nkids; i++) {
+ uint64_t guid;
+
+ rc = nvlist_find(kids, ZPOOL_CONFIG_GUID,
+ DATA_TYPE_UINT64, NULL, &guid);
+ if (rc != 0)
+ return (rc);
+ rc = vdev_init(guid, kids, &vdev);
+ if (rc != 0)
return (rc);
- if (is_new)
- STAILQ_INSERT_TAIL(&vdev->v_children, kid,
- v_childlink);
+
+ vdev->v_spa = spa;
+ vdev->v_top = top_vdev;
+ vdev_insert(top_vdev, vdev);
+
kids = nvlist_next(kids);
}
} else {
- vdev->v_nchildren = 0;
+ /*
+ * When there are no children, nvlist_find() does return
+ * error, reset it because leaf devices have no children.
+ */
+ rc = 0;
}
- if (vdevp)
- *vdevp = vdev;
- return (0);
+ return (rc);
+}
+
+static int
+vdev_init_from_label(spa_t *spa, const unsigned char *nvlist)
+{
+ uint64_t pool_guid, top_guid;
+ const unsigned char *vdevs;
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
+ NULL, &pool_guid) ||
+ nvlist_find(nvlist, ZPOOL_CONFIG_TOP_GUID, DATA_TYPE_UINT64,
+ NULL, &top_guid) ||
+ nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
+ NULL, &vdevs)) {
+ printf("ZFS: can't find vdev details\n");
+ return (ENOENT);
+ }
+
+ return (vdev_from_nvlist(spa, top_guid, vdevs));
}
static void
@@ -1288,6 +1409,10 @@ vdev_set_state(vdev_t *vdev)
int good_kids;
int bad_kids;
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ vdev_set_state(kid);
+ }
+
/*
* A mirror or raidz is healthy if all its kids are healthy. A
* mirror is degraded if any of its kids is healthy; a raidz
@@ -1322,6 +1447,104 @@ vdev_set_state(vdev_t *vdev)
}
}
+static int
+vdev_update_from_nvlist(uint64_t top_guid, const unsigned char *nvlist)
+{
+ vdev_t *vdev;
+ const unsigned char *kids;
+ int rc, nkids;
+
+ /* Update top vdev. */
+ vdev = vdev_find(top_guid);
+ if (vdev != NULL)
+ vdev_set_initial_state(vdev, nvlist);
+
+ /* Update children if there are any. */
+ rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
+ &nkids, &kids);
+ if (rc == 0) {
+ for (int i = 0; i < nkids; i++) {
+ uint64_t guid;
+
+ rc = nvlist_find(kids, ZPOOL_CONFIG_GUID,
+ DATA_TYPE_UINT64, NULL, &guid);
+ if (rc != 0)
+ break;
+
+ vdev = vdev_find(guid);
+ if (vdev != NULL)
+ vdev_set_initial_state(vdev, kids);
+
+ kids = nvlist_next(kids);
+ }
+ } else {
+ rc = 0;
+ }
+
+ return (rc);
+}
+
+static int
+vdev_init_from_nvlist(spa_t *spa, const unsigned char *nvlist)
+{
+ uint64_t pool_guid, vdev_children;
+ const unsigned char *vdevs, *kids;
+ int rc, nkids;
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
+ NULL, &pool_guid) ||
+ nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_CHILDREN, DATA_TYPE_UINT64,
+ NULL, &vdev_children) ||
+ nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
+ NULL, &vdevs)) {
+ printf("ZFS: can't find vdev details\n");
+ return (ENOENT);
+ }
+
+ /* Wrong guid?! */
+ if (spa->spa_guid != pool_guid)
+ return (EINVAL);
+
+ spa->spa_root_vdev->v_nchildren = vdev_children;
+
+ rc = nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
+ &nkids, &kids);
+
+ /*
+ * MOS config has at least one child for root vdev.
+ */
+ if (rc != 0)
+ return (rc);
+
+ for (int i = 0; i < nkids; i++) {
+ uint64_t guid;
+ vdev_t *vdev;
+
+ rc = nvlist_find(kids, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
+ NULL, &guid);
+ if (rc != 0)
+ break;
+ vdev = vdev_find(guid);
+ /*
+ * Top level vdev is missing, create it.
+ */
+ if (vdev == NULL)
+ rc = vdev_from_nvlist(spa, guid, kids);
+ else
+ rc = vdev_update_from_nvlist(guid, kids);
+ if (rc != 0)
+ break;
+ kids = nvlist_next(kids);
+ }
+
+ /*
+ * Re-evaluate top-level vdev state.
+ */
+ vdev_set_state(spa->spa_root_vdev);
+
+ return (rc);
+}
+
static spa_t *
spa_find_by_guid(uint64_t guid)
{
@@ -1362,7 +1585,7 @@ spa_get_primary_vdev(const spa_t *spa)
spa = spa_get_primary();
if (spa == NULL)
return (NULL);
- vdev = STAILQ_FIRST(&spa->spa_vdevs);
+ vdev = spa->spa_root_vdev;
if (vdev == NULL)
return (NULL);
for (kid = STAILQ_FIRST(&vdev->v_children); kid != NULL;
@@ -1382,8 +1605,14 @@ spa_create(uint64_t guid, const char *name)
free(spa);
return (NULL);
}
- STAILQ_INIT(&spa->spa_vdevs);
spa->spa_guid = guid;
+ spa->spa_root_vdev = vdev_create(guid, NULL);
+ if (spa->spa_root_vdev == NULL) {
+ free(spa->spa_name);
+ free(spa);
+ return (NULL);
+ }
+ spa->spa_root_vdev->v_name = strdup("root");
STAILQ_INSERT_TAIL(&zfs_pools, spa, spa_link);
return (spa);
@@ -1460,6 +1689,7 @@ spa_status(spa_t *spa)
{
static char bootfs[ZFS_MAXNAMELEN];
uint64_t rootid;
+ vdev_list_t *vlist;
vdev_t *vdev;
int good_kids, bad_kids, degraded_kids, ret;
vdev_state_t state;
@@ -1488,7 +1718,8 @@ spa_status(spa_t *spa)
good_kids = 0;
degraded_kids = 0;
bad_kids = 0;
- STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) {
+ vlist = &spa->spa_root_vdev->v_children;
+ STAILQ_FOREACH(vdev, vlist, v_childlink) {
if (vdev->v_state == VDEV_STATE_HEALTHY)
good_kids++;
else if (vdev->v_state == VDEV_STATE_DEGRADED)
@@ -1506,7 +1737,8 @@ spa_status(spa_t *spa)
ret = print_state(0, spa->spa_name, state);
if (ret != 0)
return (ret);
- STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) {
+
+ STAILQ_FOREACH(vdev, vlist, v_childlink) {
ret = vdev_status(vdev, 1);
if (ret != 0)
return (ret);
@@ -1698,15 +1930,14 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
{
vdev_t vtmp;
spa_t *spa;
- vdev_t *vdev, *top_vdev, *pool_vdev;
+ vdev_t *vdev;
unsigned char *nvlist;
uint64_t val;
- uint64_t guid;
+ uint64_t guid, vdev_children;
uint64_t pool_txg, pool_guid;
const char *pool_name;
- const unsigned char *vdevs;
const unsigned char *features;
- int rc, is_newer;
+ int rc;
/*
* Load the vdev label and figure out which
@@ -1778,18 +2009,17 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
*/
spa = spa_find_by_guid(pool_guid);
if (spa == NULL) {
+ nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_CHILDREN,
+ DATA_TYPE_UINT64, NULL, &vdev_children);
spa = spa_create(pool_guid, pool_name);
if (spa == NULL) {
free(nvlist);
return (ENOMEM);
}
+ spa->spa_root_vdev->v_nchildren = vdev_children;
}
- if (pool_txg > spa->spa_txg) {
+ if (pool_txg > spa->spa_txg)
spa->spa_txg = pool_txg;
- is_newer = 1;
- } else {
- is_newer = 0;
- }
/*
* Get the vdev tree and create our in-core copy of it.
@@ -1809,39 +2039,25 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
return (EIO);
}
- if (nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
- NULL, &vdevs)) {
- free(nvlist);
- return (EIO);
- }
-
- rc = vdev_init_from_nvlist(vdevs, NULL, &top_vdev, is_newer);
+ rc = vdev_init_from_label(spa, nvlist);
free(nvlist);
if (rc != 0)
return (rc);
/*
- * Add the toplevel vdev to the pool if its not already there.
- */
- STAILQ_FOREACH(pool_vdev, &spa->spa_vdevs, v_childlink)
- if (top_vdev == pool_vdev)
- break;
-
- if (!pool_vdev && top_vdev) {
- top_vdev->spa = spa;
- STAILQ_INSERT_TAIL(&spa->spa_vdevs, top_vdev, v_childlink);
- }
-
- /*
* We should already have created an incomplete vdev for this
* vdev. Find it and initialise it with our read proc.
*/
vdev = vdev_find(guid);
- if (vdev) {
+ if (vdev != NULL) {
vdev->v_phys_read = phys_read;
vdev->v_read_priv = read_priv;
- vdev->v_state = VDEV_STATE_HEALTHY;
vdev->v_psize = vtmp.v_psize;
+ /*
+ * If no other state is set, mark vdev healthy.
+ */
+ if (vdev->v_state == VDEV_STATE_UNKNOWN)
+ vdev->v_state = VDEV_STATE_HEALTHY;
} else {
printf("ZFS: inconsistent nvlist contents\n");
return (EIO);
@@ -1851,13 +2067,13 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
spa->spa_with_log = vdev->v_islog;
/* Record boot vdev for spa. */
- if (is_newer == 1)
+ if (spa->spa_boot_vdev == NULL)
spa->spa_boot_vdev = vdev;
/*
* Re-evaluate top-level vdev state.
*/
- vdev_set_state(top_vdev);
+ vdev_set_state(vdev->v_top);
/*
* Ok, we are happy with the pool so far. Lets find
@@ -1866,7 +2082,6 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
*/
vdev_uberblock_load(vdev, &spa->spa_uberblock);
- vdev->spa = spa;
if (spap != NULL)
*spap = spa;
return (0);
@@ -1961,7 +2176,8 @@ zio_read(const spa_t *spa, const blkptr_t *bp, void *buf)
for (i = 0; i < SPA_DVAS_PER_BP; i++) {
const dva_t *dva = &bp->blk_dva[i];
vdev_t *vdev;
- int vdevid;
+ vdev_list_t *vlist;
+ uint64_t vdevid;
off_t offset;
if (!dva->dva_word[0] && !dva->dva_word[1])
@@ -1969,7 +2185,8 @@ zio_read(const spa_t *spa, const blkptr_t *bp, void *buf)
vdevid = DVA_GET_VDEV(dva);
offset = DVA_GET_OFFSET(dva);
- STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) {
+ vlist = &spa->spa_root_vdev->v_children;
+ STAILQ_FOREACH(vdev, vlist, v_childlink) {
if (vdev->v_id == vdevid)
break;
}
@@ -1978,7 +2195,7 @@ zio_read(const spa_t *spa, const blkptr_t *bp, void *buf)
size = BP_GET_PSIZE(bp);
if (vdev->v_read == vdev_raidz_read) {
- align = 1ULL << vdev->v_top->v_ashift;
+ align = 1ULL << vdev->v_ashift;
if (P2PHASE(size, align) != 0)
size = P2ROUNDUP(size, align);
}
@@ -3026,9 +3243,7 @@ zfs_spa_init(spa_t *spa)
dnode_phys_t dir;
uint64_t config_object;
unsigned char *nvlist;
- char *type;
- const unsigned char *nv;
- int nkids, rc;
+ int rc;
if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
printf("ZFS: can't read MOS of pool %s\n", spa->spa_name);
@@ -3066,62 +3281,11 @@ zfs_spa_init(spa_t *spa)
if (rc != 0)
return (rc);
- /* Update vdevs from MOS config. */
- if (nvlist_find(nvlist + 4, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
- NULL, &nv)) {
- rc = EIO;
- goto done;
- }
-
- if (nvlist_find(nv, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING, NULL, &type)) {
- printf("ZFS: can't find vdev details\n");
- rc = ENOENT;
- goto done;
- }
- if (strcmp(type, VDEV_TYPE_ROOT) != 0) {
- rc = ENOENT;
- goto done;
- }
-
- rc = nvlist_find(nv, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
- &nkids, &nv);
- if (rc != 0)
- goto done;
-
- for (int i = 0; i < nkids; i++) {
- vdev_t *vd, *prev, *kid = NULL;
- rc = vdev_init_from_nvlist(nv, NULL, &kid, 0);
- if (rc != 0) {
- printf("vdev_init_from_nvlist: %d\n", rc);
- break;
- }
- kid->spa = spa;
- prev = NULL;
- STAILQ_FOREACH(vd, &spa->spa_vdevs, v_childlink) {
- /* Already present? */
- if (kid->v_id == vd->v_id) {
- kid = NULL;
- break;
- }
- if (vd->v_id > kid->v_id) {
- if (prev == NULL) {
- STAILQ_INSERT_HEAD(&spa->spa_vdevs,
- kid, v_childlink);
- } else {
- STAILQ_INSERT_AFTER(&spa->spa_vdevs,
- prev, kid, v_childlink);
- }
- kid = NULL;
- break;
- }
- prev = vd;
- }
- if (kid != NULL)
- STAILQ_INSERT_TAIL(&spa->spa_vdevs, kid, v_childlink);
- nv = nvlist_next(nv);
- }
- rc = 0;
-done:
+ /*
+ * Update vdevs from MOS config. Note, we do skip encoding bytes
+ * here. See also vdev_label_read_config().
+ */
+ rc = vdev_init_from_nvlist(spa, nvlist + 4);
free(nvlist);
return (rc);
}
diff --git a/usr/src/boot/sys/boot/forth/loader.conf b/usr/src/boot/sys/boot/forth/loader.conf
index b1e0fb6a16..78028c88fe 100644
--- a/usr/src/boot/sys/boot/forth/loader.conf
+++ b/usr/src/boot/sys/boot/forth/loader.conf
@@ -58,7 +58,7 @@ autoboot_delay="10" # Delay in seconds before autobooting,
beastie_disable="NO" # Turn the beastie boot menu on and off
loader_logo="illumos" # Desired logo: orbbw, orb, fbsdbw, beastiebw, beastie, none
#loader_brand="illumos" # brand name
-#console="text" # A comma separated list of console(s)
+console="text,ttya,ttyb,ttyc,ttyd" # A comma separated list of console(s)
#currdev="disk1s1a" # Set the current device
module_path="/platform/i86pc/${ISADIR}/" # Set the module search path
#prompt="\\${interpret}" # Set the command prompt
diff --git a/usr/src/boot/sys/boot/i386/libi386/biosdisk.c b/usr/src/boot/sys/boot/i386/libi386/biosdisk.c
index 84ab603de6..3d1ab645d5 100644
--- a/usr/src/boot/sys/boot/i386/libi386/biosdisk.c
+++ b/usr/src/boot/sys/boot/i386/libi386/biosdisk.c
@@ -392,6 +392,7 @@ bc_add(int biosdev)
bd->bd_flags = BD_CDROM;
bd->bd_unit = biosdev;
+ bd->bd_sectorsize = 2048;
/*
* Ignore result from bd_int13probe(), we will use local
@@ -559,7 +560,8 @@ bd_int13probe(bdinfo_t *bd)
bd->bd_flags |= BD_MODEEDD3;
/* Default sector size */
- bd->bd_sectorsize = BIOSDISK_SECSIZE;
+ if (bd->bd_sectorsize == 0)
+ bd->bd_sectorsize = BIOSDISK_SECSIZE;
/*
* Test if the floppy device is present, so we can avoid receiving
diff --git a/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h b/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h
index 6b629f8fe5..c57181b670 100644
--- a/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h
+++ b/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h
@@ -763,6 +763,7 @@ typedef enum {
#define ZPOOL_CONFIG_IS_LOG "is_log"
#define ZPOOL_CONFIG_TIMESTAMP "timestamp" /* not stored on disk */
#define ZPOOL_CONFIG_FEATURES_FOR_READ "features_for_read"
+#define ZPOOL_CONFIG_VDEV_CHILDREN "vdev_children"
/*
* The persistent vdev state is stored as separate values rather than a single
@@ -1765,13 +1766,13 @@ typedef struct vdev {
int v_ashift; /* offset to block shift */
int v_nparity; /* # parity for raidz */
struct vdev *v_top; /* parent vdev */
- int v_nchildren; /* # children */
+ size_t v_nchildren; /* # children */
vdev_state_t v_state; /* current state */
vdev_phys_read_t *v_phys_read; /* read from raw leaf vdev */
vdev_read_t *v_read; /* read from vdev */
void *v_read_priv; /* private data for read function */
boolean_t v_islog;
- struct spa *spa; /* link to spa */
+ struct spa *v_spa; /* link to spa */
/*
* Values stored in the config for an indirect or removing vdev.
*/
@@ -1790,11 +1791,10 @@ typedef struct spa {
uint64_t spa_guid; /* pool guid */
uint64_t spa_txg; /* most recent transaction */
struct uberblock spa_uberblock; /* best uberblock so far */
- vdev_list_t spa_vdevs; /* list of all toplevel vdevs */
+ vdev_t *spa_root_vdev; /* toplevel vdev container */
objset_phys_t spa_mos; /* MOS for this pool */
zio_cksum_salt_t spa_cksum_salt; /* secret salt for cksum */
void *spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS];
- int spa_inited; /* initialized */
vdev_t *spa_boot_vdev; /* boot device for kernel */
boolean_t spa_with_log; /* this pool has log */
} spa_t;
diff --git a/usr/src/boot/sys/cddl/boot/zfs/zfssubr.c b/usr/src/boot/sys/cddl/boot/zfs/zfssubr.c
index 4cfd337213..0d509ed4a1 100644
--- a/usr/src/boot/sys/cddl/boot/zfs/zfssubr.c
+++ b/usr/src/boot/sys/cddl/boot/zfs/zfssubr.c
@@ -1643,7 +1643,7 @@ reconstruct:
int rv;
if (data_errors == 0) {
- rv = raidz_checksum_verify(vd->spa, bp, data, bytes);
+ rv = raidz_checksum_verify(vd->v_spa, bp, data, bytes);
if (rv == 0) {
/*
* If we read parity information (unnecessarily
@@ -1689,7 +1689,7 @@ reconstruct:
code = vdev_raidz_reconstruct(rm, tgts, n);
- rv = raidz_checksum_verify(vd->spa, bp, data, bytes);
+ rv = raidz_checksum_verify(vd->v_spa, bp, data, bytes);
if (rv == 0) {
/*
* If we read more parity disks than were used
@@ -1764,7 +1764,7 @@ reconstruct:
if (total_errors > rm->rm_firstdatacol) {
error = EIO;
} else if (total_errors < rm->rm_firstdatacol &&
- (code = vdev_raidz_combrec(vd->spa, rm, bp, data, offset, bytes,
+ (code = vdev_raidz_combrec(vd->v_spa, rm, bp, data, offset, bytes,
total_errors, data_errors)) != 0) {
/*
* If we didn't use all the available parity for the
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 3d5c7e4e30..7a410a3a49 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -83,6 +83,7 @@ COMMON_SUBDIRS= \
cal \
captoinfo \
cat \
+ ccidadm \
cdrw \
cfgadm \
checkeq \
@@ -378,6 +379,7 @@ COMMON_SUBDIRS= \
sgs \
sh \
shcomp \
+ sleep \
smbios \
smbsrv \
smserverd \
diff --git a/usr/src/cmd/addbadsec/Makefile b/usr/src/cmd/addbadsec/Makefile
index 54fc4fa23b..4a05f046ae 100644
--- a/usr/src/cmd/addbadsec/Makefile
+++ b/usr/src/cmd/addbadsec/Makefile
@@ -25,7 +25,7 @@
#
# Copyright (c) 2018, Joyent, Inc.
-PROG= addbadsec
+PROG= addbadsec
OBJECTS= addbadsec.o ix_altsctr.o
SRCS= $(OBJECTS:.o=.c)
@@ -43,7 +43,7 @@ SMATCH=off
all: $(PROG)
$(PROG): $(OBJECTS)
- $(CC) -o $@ $(OBJECTS) $(LDFLAGS) $(LDLIBS)
+ $(LINK.c) -o $@ $(OBJECTS) $(LDLIBS)
$(POST_PROCESS)
install: all $(ROOTPROG)
diff --git a/usr/src/cmd/audio/audioconvert/Makefile b/usr/src/cmd/audio/audioconvert/Makefile
index 3cdd2b4e0e..051b918766 100644
--- a/usr/src/cmd/audio/audioconvert/Makefile
+++ b/usr/src/cmd/audio/audioconvert/Makefile
@@ -34,6 +34,7 @@ include ../../Makefile.cmd
INCLUDES += -I../include -I.
CPPFLAGS += $(INCLUDES)
+CCFLAGS += -_gcc4=-std=gnu++0x
PROGSRCS= convert.cc file.cc main.cc parse.cc
diff --git a/usr/src/cmd/audio/include/AudioError.h b/usr/src/cmd/audio/include/AudioError.h
index de4e67f986..76d127e2c2 100644
--- a/usr/src/cmd/audio/include/AudioError.h
+++ b/usr/src/cmd/audio/include/AudioError.h
@@ -27,8 +27,6 @@
#ifndef _MULTIMEDIA_AUDIOERROR_H
#define _MULTIMEDIA_AUDIOERROR_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <locale.h>
#include <errno.h>
#include <audio_errno.h> /* to get enum for error codes */
@@ -52,6 +50,7 @@ private:
public:
int sys; // system error code
+ AudioError(const AudioError&) = default;
inline AudioError(audioerror_t val = AUDIO_SUCCESS): // Constructor
code(val), sys(0)
{ if (code == AUDIO_UNIXERROR) sys = errno; }
diff --git a/usr/src/cmd/audio/include/AudioTypes.h b/usr/src/cmd/audio/include/AudioTypes.h
index 1eb10d92c0..2d8971c04a 100644
--- a/usr/src/cmd/audio/include/AudioTypes.h
+++ b/usr/src/cmd/audio/include/AudioTypes.h
@@ -145,6 +145,7 @@ public:
{ val += y; return (*this); }
inline Double& operator -= (double y)
{ val -= y; return (*this); }
+ Double& operator=(const Double&) = default;
};
// inline double fabs(double x)
diff --git a/usr/src/cmd/audio/utilities/Makefile b/usr/src/cmd/audio/utilities/Makefile
index 6d62590594..4038386710 100644
--- a/usr/src/cmd/audio/utilities/Makefile
+++ b/usr/src/cmd/audio/utilities/Makefile
@@ -33,6 +33,7 @@ INCLUDES += -I../include
CPPFLAGS += $(INCLUDES)
+CCFLAGS += -_gcc4=-std=gnu++0x
CFLAGS += $(CCVERBOSE)
CERRWARN += $(CNOWARN_UNINIT)
CERRWARN += -_gcc=-Wno-unused-variable
diff --git a/usr/src/cmd/auditreduce/Makefile b/usr/src/cmd/auditreduce/Makefile
index 1c39fe869f..f8b5cd7f0e 100644
--- a/usr/src/cmd/auditreduce/Makefile
+++ b/usr/src/cmd/auditreduce/Makefile
@@ -54,7 +54,7 @@ all: $(PROG)
install: all $(ROOTUSRSBINPROG)
$(PROG): $(OBJS)
- $(CC) -o $(PROG) $(OBJS) $(LDFLAGS) $(LDLIBS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
$(POST_PROCESS)
toktable.o: $(TABLEDIR)/toktable.c
diff --git a/usr/src/cmd/bart/Makefile b/usr/src/cmd/bart/Makefile
index 47938144ab..e5ac75c33b 100644
--- a/usr/src/cmd/bart/Makefile
+++ b/usr/src/cmd/bart/Makefile
@@ -52,7 +52,7 @@ ROOTLIBDIFFH= $(DIFFH:%=$(ROOTLIB)/%)
all: $(PROG)
$(PROG): $(OBJS)
- $(CC) -o $(PROG) $(OBJS) $(LDFLAGS) $(LDLIBS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
$(POST_PROCESS)
clean:
diff --git a/usr/src/cmd/ccidadm/Makefile b/usr/src/cmd/ccidadm/Makefile
new file mode 100644
index 0000000000..de9761ff9d
--- /dev/null
+++ b/usr/src/cmd/ccidadm/Makefile
@@ -0,0 +1,51 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+PROG= ccidadm
+
+include ../Makefile.cmd
+include ../Makefile.ctf
+
+ROOTCMDDIR = $(ROOTLIB)/ccid
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lofmt -lcmdutils
+SRCS = ccidadm.c atr.c
+OBJS = $(SRCS:%.c=%.o)
+CPPFLAGS += -I$(SRC)/common/ccid
+
+ROOTCCIDFILES = $(PROG:%=$(ROOTCMDDIR)/%)
+
+.KEEP_STATE:
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: %.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+%.o: $(SRC)/common/ccid/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+ $(RM) $(OBJS)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/ccidadm/ccidadm.c b/usr/src/cmd/ccidadm/ccidadm.c
new file mode 100644
index 0000000000..1d3c2bcbfc
--- /dev/null
+++ b/usr/src/cmd/ccidadm/ccidadm.c
@@ -0,0 +1,844 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Print out information about a CCID device.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <err.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <ofmt.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <libcmdutils.h>
+#include <fts.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+#include <atr.h>
+
+#define EXIT_USAGE 2
+
+static const char *ccidadm_pname;
+
+#define CCID_ROOT "/dev/ccid/"
+
+typedef enum {
+ CCIDADM_LIST_DEVICE,
+ CCIDADM_LIST_PRODUCT,
+ CCIDADM_LIST_STATE,
+ CCIDADM_LIST_TRANSPORT,
+ CCIDADM_LIST_SUPPORTED,
+} ccidadm_list_index_t;
+
+typedef struct ccidadm_pair {
+ uint32_t ccp_val;
+ const char *ccp_name;
+} ccidadm_pair_t;
+
+typedef struct ccid_list_ofmt_arg {
+ const char *cloa_name;
+ uccid_cmd_status_t *cloa_status;
+} ccid_list_ofmt_arg_t;
+
+/*
+ * Attempt to open a CCID slot specified by a user. In general, we expect that
+ * users will use a path like "ccid0/slot0". However, they may also specify a
+ * full path. If the card boolean is set to true, that means that they may have
+ * just specified "ccid0", so we need to try to open up the default slot.
+ */
+static int
+ccidadm_open(const char *base, boolean_t card)
+{
+ int fd;
+ char buf[PATH_MAX];
+
+ /*
+ * If it's an absolute path, just try to open it.
+ */
+ if (base[0] == '/') {
+ return (open(base, O_RDWR));
+ }
+
+ /*
+ * For a card, try to append slot0 first.
+ */
+ if (card) {
+ if (snprintf(buf, sizeof (buf), "%s/%s/slot0", CCID_ROOT,
+ base) >= sizeof (buf)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ if ((fd = open(buf, O_RDWR)) >= 0) {
+ return (fd);
+ }
+
+ if (errno != ENOENT && errno != ENOTDIR) {
+ return (fd);
+ }
+ }
+
+ if (snprintf(buf, sizeof (buf), "%s/%s", CCID_ROOT, base) >=
+ sizeof (buf)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ return (open(buf, O_RDWR));
+}
+
+static void
+ccidadm_iter(boolean_t readeronly, boolean_t newline,
+ void(*cb)(int, const char *, void *), void *arg)
+{
+ FTS *fts;
+ FTSENT *ent;
+ char *const paths[] = { CCID_ROOT, NULL };
+ int fd;
+ boolean_t first = B_TRUE;
+
+ fts = fts_open(paths, FTS_LOGICAL | FTS_NOCHDIR, NULL);
+ if (fts == NULL) {
+ err(EXIT_FAILURE, "failed to create directory stream");
+ }
+
+ while ((ent = fts_read(fts)) != NULL) {
+ const char *name;
+
+ /* Skip the root and post-order dirs */
+ if (ent->fts_level == 0 || ent->fts_info == FTS_DP) {
+ continue;
+ }
+ if (readeronly && ent->fts_level != 1) {
+ continue;
+ } else if (!readeronly && ent->fts_level != 2) {
+ continue;
+ }
+
+ if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_NS) {
+ warn("skipping %s, failed to get information: %s",
+ ent->fts_name, strerror(ent->fts_errno));
+ continue;
+ }
+
+ name = ent->fts_path + strlen(CCID_ROOT);
+ if ((fd = ccidadm_open(name, readeronly)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", name);
+ }
+
+ if (!first && newline) {
+ (void) printf("\n");
+ }
+ first = B_FALSE;
+ cb(fd, name, arg);
+ (void) close(fd);
+ }
+
+ (void) fts_close(fts);
+}
+
+static void
+ccidadm_list_slot_status_str(uccid_cmd_status_t *ucs, char *buf, uint_t buflen)
+{
+ if (!(ucs->ucs_status & UCCID_STATUS_F_CARD_PRESENT)) {
+ (void) snprintf(buf, buflen, "missing");
+ return;
+ }
+
+ if (ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE) {
+ (void) snprintf(buf, buflen, "activated");
+ return;
+ }
+
+ (void) snprintf(buf, buflen, "unactivated");
+}
+
+static boolean_t
+ccidadm_list_slot_transport_str(uccid_cmd_status_t *ucs, char *buf,
+ uint_t buflen)
+{
+ const char *prot;
+ const char *tran;
+ uint_t bits = CCID_CLASS_F_TPDU_XCHG | CCID_CLASS_F_SHORT_APDU_XCHG |
+ CCID_CLASS_F_EXT_APDU_XCHG;
+
+ switch (ucs->ucs_class.ccd_dwFeatures & bits) {
+ case 0:
+ tran = "character";
+ break;
+ case CCID_CLASS_F_TPDU_XCHG:
+ tran = "TPDU";
+ break;
+ case CCID_CLASS_F_SHORT_APDU_XCHG:
+ case CCID_CLASS_F_EXT_APDU_XCHG:
+ tran = "APDU";
+ break;
+ default:
+ tran = "unknown";
+ break;
+ }
+
+ if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) != 0) {
+ switch (ucs->ucs_prot) {
+ case UCCID_PROT_T0:
+ prot = " (T=0)";
+ break;
+ case UCCID_PROT_T1:
+ prot = " (T=1)";
+ break;
+ default:
+ prot = "";
+ break;
+ }
+ } else {
+ prot = "";
+ }
+
+ return (snprintf(buf, buflen, "%s%s", tran, prot) < buflen);
+}
+
+static boolean_t
+ccidadm_list_slot_usable_str(uccid_cmd_status_t *ucs, char *buf,
+ uint_t buflen)
+{
+ char *un = "";
+ ccid_class_features_t feat;
+ uint_t prot = CCID_CLASS_F_SHORT_APDU_XCHG | CCID_CLASS_F_EXT_APDU_XCHG;
+ uint_t param = CCID_CLASS_F_AUTO_PARAM_NEG | CCID_CLASS_F_AUTO_PPS;
+ uint_t clock = CCID_CLASS_F_AUTO_BAUD | CCID_CLASS_F_AUTO_ICC_CLOCK;
+
+ feat = ucs->ucs_class.ccd_dwFeatures;
+
+ if ((feat & prot) == 0 ||
+ (feat & param) != param ||
+ (feat & clock) != clock) {
+ un = "un";
+ }
+
+ return (snprintf(buf, buflen, "%ssupported", un) < buflen);
+}
+
+static boolean_t
+ccidadm_list_ofmt_cb(ofmt_arg_t *ofmt, char *buf, uint_t buflen)
+{
+ ccid_list_ofmt_arg_t *cloa = ofmt->ofmt_cbarg;
+
+ switch (ofmt->ofmt_id) {
+ case CCIDADM_LIST_DEVICE:
+ if (snprintf(buf, buflen, "%s", cloa->cloa_name) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case CCIDADM_LIST_PRODUCT:
+ if (snprintf(buf, buflen, "%s",
+ cloa->cloa_status->ucs_product) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case CCIDADM_LIST_STATE:
+ ccidadm_list_slot_status_str(cloa->cloa_status, buf, buflen);
+ break;
+ case CCIDADM_LIST_TRANSPORT:
+ return (ccidadm_list_slot_transport_str(cloa->cloa_status, buf,
+ buflen));
+ break;
+ case CCIDADM_LIST_SUPPORTED:
+ return (ccidadm_list_slot_usable_str(cloa->cloa_status, buf,
+ buflen));
+ break;
+ default:
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static void
+ccidadm_list_slot(int slotfd, const char *name, void *arg)
+{
+ uccid_cmd_status_t ucs;
+ ofmt_handle_t ofmt = arg;
+ ccid_list_ofmt_arg_t cloa;
+
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+
+ if (ioctl(slotfd, UCCID_CMD_STATUS, &ucs) != 0) {
+ err(EXIT_FAILURE, "failed to issue status ioctl to %s", name);
+ }
+
+ if ((ucs.ucs_status & UCCID_STATUS_F_PRODUCT_VALID) == 0) {
+ (void) strlcpy(ucs.ucs_product, "<unknown>",
+ sizeof (ucs.ucs_product));
+ }
+
+ cloa.cloa_name = name;
+ cloa.cloa_status = &ucs;
+ ofmt_print(ofmt, &cloa);
+}
+
+static ofmt_field_t ccidadm_list_fields[] = {
+ { "PRODUCT", 24, CCIDADM_LIST_PRODUCT, ccidadm_list_ofmt_cb },
+ { "DEVICE", 16, CCIDADM_LIST_DEVICE, ccidadm_list_ofmt_cb },
+ { "CARD STATE", 12, CCIDADM_LIST_STATE, ccidadm_list_ofmt_cb },
+ { "TRANSPORT", 12, CCIDADM_LIST_TRANSPORT, ccidadm_list_ofmt_cb },
+ { "SUPPORTED", 12, CCIDADM_LIST_SUPPORTED, ccidadm_list_ofmt_cb },
+ { NULL, 0, 0, NULL }
+};
+
+static void
+ccidadm_do_list(int argc, char *argv[])
+{
+ ofmt_handle_t ofmt;
+
+ if (argc != 0) {
+ errx(EXIT_USAGE, "list command does not take arguments\n");
+ }
+
+ if (ofmt_open(NULL, ccidadm_list_fields, 0, 0, &ofmt) != OFMT_SUCCESS) {
+ errx(EXIT_FAILURE, "failed to initialize ofmt state");
+ }
+
+ ccidadm_iter(B_FALSE, B_FALSE, ccidadm_list_slot, ofmt);
+ ofmt_close(ofmt);
+}
+
+static void
+ccidadm_list_usage(FILE *out)
+{
+ (void) fprintf(out, "\tlist\n");
+}
+
+/*
+ * Print out logical information about the ICC's ATR. This includes information
+ * about what protocols it supports, required negotiation, etc.
+ */
+static void
+ccidadm_atr_props(uccid_cmd_status_t *ucs)
+{
+ int ret;
+ atr_data_t *data;
+ atr_protocol_t prots, defprot;
+ boolean_t negotiate;
+ atr_data_rate_choice_t rate;
+ uint32_t bps;
+
+ if ((data = atr_data_alloc()) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate memory for "
+ "ATR data");
+ }
+
+ ret = atr_parse(ucs->ucs_atr, ucs->ucs_atrlen, data);
+ if (ret != ATR_CODE_OK) {
+ errx(EXIT_FAILURE, "failed to parse ATR data: %s",
+ atr_strerror(ret));
+ }
+
+ prots = atr_supported_protocols(data);
+ (void) printf("ICC supports protocol(s): ");
+ if (prots == ATR_P_NONE) {
+ (void) printf("none\n");
+ atr_data_free(data);
+ return;
+ }
+
+ (void) printf("%s\n", atr_protocol_to_string(prots));
+
+ negotiate = atr_params_negotiable(data);
+ defprot = atr_default_protocol(data);
+
+ if (negotiate) {
+ (void) printf("Card protocol is negotiable; starts with "
+ "default %s parameters\n", atr_protocol_to_string(defprot));
+ } else {
+ (void) printf("Card protocol is not negotiable; starts with "
+ "specific %s parameters\n",
+ atr_protocol_to_string(defprot));
+ }
+
+ /*
+ * For each supported protocol, figure out parameters we would
+ * negotiate. We only need to warn about auto-negotiation if this
+ * is TPDU or character and specific bits are missing.
+ */
+ if (((ucs->ucs_class.ccd_dwFeatures & (CCID_CLASS_F_SHORT_APDU_XCHG |
+ CCID_CLASS_F_EXT_APDU_XCHG)) == 0) &&
+ ((ucs->ucs_class.ccd_dwFeatures & (CCID_CLASS_F_AUTO_PARAM_NEG |
+ CCID_CLASS_F_AUTO_PPS)) == 0)) {
+ (void) printf("CCID/ICC require explicit TPDU parameter/PPS "
+ "negotiation\n");
+ }
+
+ /*
+ * Determine which set of Di/Fi values we should use and how we should
+ * get there (note a reader may not have to set them).
+ */
+ rate = atr_data_rate(data, &ucs->ucs_class, NULL, 0, &bps);
+ switch (rate) {
+ case ATR_RATE_USEDEFAULT:
+ (void) printf("Reader will run ICC at the default (Di=1/Fi=1) "
+ "speed\n");
+ break;
+ case ATR_RATE_USEATR:
+ (void) printf("Reader will run ICC at ICC's Di/Fi values\n");
+ break;
+ case ATR_RATE_USEATR_SETRATE:
+ (void) printf("Reader will run ICC at ICC's Di/Fi values, but "
+ "must set data rate to %u bps\n", bps);
+ break;
+ case ATR_RATE_UNSUPPORTED:
+ (void) printf("Reader cannot run ICC due to Di/Fi mismatch\n");
+ break;
+ default:
+ (void) printf("Cannot determine Di/Fi rate, unexpected "
+ "value: %u\n", rate);
+ break;
+ }
+ if (prots & ATR_P_T0) {
+ uint8_t fi, di;
+ atr_convention_t conv;
+ atr_clock_stop_t clock;
+
+ fi = atr_fi_index(data);
+ di = atr_di_index(data);
+ conv = atr_convention(data);
+ clock = atr_clock_stop(data);
+ (void) printf("T=0 properties that would be negotiated:\n");
+ (void) printf(" + Fi/Fmax Index: %u (Fi %s/Fmax %s MHz)\n",
+ fi, atr_fi_index_to_string(fi),
+ atr_fmax_index_to_string(fi));
+ (void) printf(" + Di Index: %u (Di %s)\n", di,
+ atr_di_index_to_string(di));
+ (void) printf(" + Clock Convention: %u (%s)\n", conv,
+ atr_convention_to_string(conv));
+ (void) printf(" + Extra Guardtime: %u\n",
+ atr_extra_guardtime(data));
+ (void) printf(" + WI: %u\n", atr_t0_wi(data));
+ (void) printf(" + Clock Stop: %u (%s)\n", clock,
+ atr_clock_stop_to_string(clock));
+ }
+
+ if (prots & ATR_P_T1) {
+ uint8_t fi, di;
+ atr_clock_stop_t clock;
+ atr_t1_checksum_t cksum;
+
+ fi = atr_fi_index(data);
+ di = atr_di_index(data);
+ clock = atr_clock_stop(data);
+ cksum = atr_t1_checksum(data);
+ (void) printf("T=1 properties that would be negotiated:\n");
+ (void) printf(" + Fi/Fmax Index: %u (Fi %s/Fmax %s MHz)\n",
+ fi, atr_fi_index_to_string(fi),
+ atr_fmax_index_to_string(fi));
+ (void) printf(" + Di Index: %u (Di %s)\n", di,
+ atr_di_index_to_string(di));
+ (void) printf(" + Checksum: %s\n",
+ cksum == ATR_T1_CHECKSUM_CRC ? "CRC" : "LRC");
+ (void) printf(" + Extra Guardtime: %u\n",
+ atr_extra_guardtime(data));
+ (void) printf(" + BWI: %u\n", atr_t1_bwi(data));
+ (void) printf(" + CWI: %u\n", atr_t1_cwi(data));
+ (void) printf(" + Clock Stop: %u (%s)\n", clock,
+ atr_clock_stop_to_string(clock));
+ (void) printf(" + IFSC: %u\n", atr_t1_ifsc(data));
+ (void) printf(" + CCID Supports NAD: %s\n",
+ ucs->ucs_class.ccd_dwFeatures & CCID_CLASS_F_ALTNAD_SUP ?
+ "yes" : "no");
+ }
+
+ atr_data_free(data);
+}
+
+static void
+ccidadm_atr_verbose(uccid_cmd_status_t *ucs)
+{
+ int ret;
+ atr_data_t *data;
+
+ if ((data = atr_data_alloc()) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate memory for "
+ "ATR data");
+ }
+
+ ret = atr_parse(ucs->ucs_atr, ucs->ucs_atrlen, data);
+ if (ret != ATR_CODE_OK) {
+ errx(EXIT_FAILURE, "failed to parse ATR data: %s",
+ atr_strerror(ret));
+ }
+ atr_data_dump(data, stdout);
+ atr_data_free(data);
+}
+
+typedef struct cciadm_atr_args {
+ boolean_t caa_hex;
+ boolean_t caa_props;
+ boolean_t caa_verbose;
+} ccidadm_atr_args_t;
+
+static void
+ccidadm_atr_fetch(int fd, const char *name, void *arg)
+{
+ uccid_cmd_status_t ucs;
+ ccidadm_atr_args_t *caa = arg;
+
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+
+ if (ioctl(fd, UCCID_CMD_STATUS, &ucs) != 0) {
+ err(EXIT_FAILURE, "failed to issue status ioctl to %s",
+ name);
+ }
+
+ if (ucs.ucs_atrlen == 0) {
+ warnx("slot %s has no card inserted or activated", name);
+ return;
+ }
+
+ (void) printf("ATR for %s (%u bytes):\n", name, ucs.ucs_atrlen);
+ if (caa->caa_props) {
+ ccidadm_atr_props(&ucs);
+ }
+
+ if (caa->caa_hex) {
+ atr_data_hexdump(ucs.ucs_atr, ucs.ucs_atrlen, stdout);
+ }
+
+ if (caa->caa_verbose) {
+ ccidadm_atr_verbose(&ucs);
+ }
+}
+
+static void
+ccidadm_do_atr(int argc, char *argv[])
+{
+ uint_t i;
+ int c;
+ ccidadm_atr_args_t caa;
+
+ bzero(&caa, sizeof (caa));
+ optind = 0;
+ while ((c = getopt(argc, argv, "vx")) != -1) {
+ switch (c) {
+ case 'v':
+ caa.caa_verbose = B_TRUE;
+ break;
+ case 'x':
+ caa.caa_hex = B_TRUE;
+ break;
+ case ':':
+ errx(EXIT_USAGE, "Option -%c requires an argument\n",
+ optopt);
+ break;
+ case '?':
+ errx(EXIT_USAGE, "Unknown option: -%c\n", optopt);
+ break;
+ }
+ }
+
+ if (!caa.caa_verbose && !caa.caa_props && !caa.caa_hex) {
+ caa.caa_props = B_TRUE;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ ccidadm_iter(B_FALSE, B_TRUE, ccidadm_atr_fetch, &caa);
+ return;
+ }
+
+ for (i = 0; i < argc; i++) {
+ int fd;
+
+ if ((fd = ccidadm_open(argv[i], B_FALSE)) < 0) {
+ warn("failed to open %s", argv[i]);
+ errx(EXIT_FAILURE, "valid CCID slot?");
+ }
+
+ ccidadm_atr_fetch(fd, argv[i], &caa);
+ (void) close(fd);
+ if (i + 1 < argc) {
+ (void) printf("\n");
+ }
+ }
+}
+
+static void
+ccidadm_atr_usage(FILE *out)
+{
+ (void) fprintf(out, "\tatr [-vx]\t[device] ...\n");
+}
+
+static void
+ccidadm_print_pairs(uint32_t val, ccidadm_pair_t *ccp)
+{
+ while (ccp->ccp_name != NULL) {
+ if ((val & ccp->ccp_val) == ccp->ccp_val) {
+ (void) printf(" + %s\n", ccp->ccp_name);
+ }
+ ccp++;
+ }
+}
+
+static ccidadm_pair_t ccidadm_p_protocols[] = {
+ { 0x01, "T=0" },
+ { 0x02, "T=1" },
+ { 0x0, NULL }
+};
+
+static ccidadm_pair_t ccidadm_p_voltages[] = {
+ { CCID_CLASS_VOLT_5_0, "5.0 V" },
+ { CCID_CLASS_VOLT_3_0, "3.0 V" },
+ { CCID_CLASS_VOLT_1_8, "1.8 V" },
+ { 0x0, NULL }
+};
+
+static ccidadm_pair_t ccidadm_p_syncprots[] = {
+ { 0x01, "2-Wire Support" },
+ { 0x02, "3-Wire Support" },
+ { 0x04, "I2C Support" },
+ { 0x0, NULL }
+};
+
+static ccidadm_pair_t ccidadm_p_mechanical[] = {
+ { CCID_CLASS_MECH_CARD_ACCEPT, "Card Accept Mechanism" },
+ { CCID_CLASS_MECH_CARD_EJECT, "Card Eject Mechanism" },
+ { CCID_CLASS_MECH_CARD_CAPTURE, "Card Capture Mechanism" },
+ { CCID_CLASS_MECH_CARD_LOCK, "Card Lock/Unlock Mechanism" },
+ { 0x0, NULL }
+};
+
+static ccidadm_pair_t ccidadm_p_features[] = {
+ { CCID_CLASS_F_AUTO_PARAM_ATR,
+ "Automatic parameter configuration based on ATR data" },
+ { CCID_CLASS_F_AUTO_ICC_ACTIVATE,
+ "Automatic activation on ICC insertion" },
+ { CCID_CLASS_F_AUTO_ICC_VOLTAGE, "Automatic ICC voltage selection" },
+ { CCID_CLASS_F_AUTO_ICC_CLOCK,
+ "Automatic ICC clock frequency change" },
+ { CCID_CLASS_F_AUTO_BAUD, "Automatic baud rate change" },
+ { CCID_CLASS_F_AUTO_PARAM_NEG,
+ "Automatic parameter negotiation by CCID" },
+ { CCID_CLASS_F_AUTO_PPS, "Automatic PPS made by CCID" },
+ { CCID_CLASS_F_ICC_CLOCK_STOP, "CCID can set ICC in clock stop mode" },
+ { CCID_CLASS_F_ALTNAD_SUP, "NAD value other than zero accepted" },
+ { CCID_CLASS_F_AUTO_IFSD, "Automatic IFSD exchange" },
+ { CCID_CLASS_F_TPDU_XCHG, "TPDU support" },
+ { CCID_CLASS_F_SHORT_APDU_XCHG, "Short APDU support" },
+ { CCID_CLASS_F_EXT_APDU_XCHG, "Short and Extended APDU support" },
+ { CCID_CLASS_F_WAKE_UP, "USB Wake Up signaling support" },
+ { 0x0, NULL }
+};
+
+static ccidadm_pair_t ccidadm_p_pin[] = {
+ { CCID_CLASS_PIN_VERIFICATION, "PIN verification" },
+ { CCID_CLASS_PIN_MODIFICATION, "PIN modification" },
+ { 0x0, NULL }
+};
+
+static void
+ccidadm_reader_print(int fd, const char *name, void *unused __unused)
+{
+ uccid_cmd_status_t ucs;
+ ccid_class_descr_t *cd;
+ char nnbuf[NN_NUMBUF_SZ + 1];
+
+ bzero(&ucs, sizeof (uccid_cmd_status_t));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+
+ if (ioctl(fd, UCCID_CMD_STATUS, &ucs) != 0) {
+ err(EXIT_FAILURE, "failed to issue status ioctl to %s",
+ name);
+ }
+
+ cd = &ucs.ucs_class;
+ (void) printf("Reader %s, CCID class v%u.%u device:\n", name,
+ CCID_VERSION_MAJOR(cd->ccd_bcdCCID),
+ CCID_VERSION_MINOR(cd->ccd_bcdCCID));
+
+ if ((ucs.ucs_status & UCCID_STATUS_F_PRODUCT_VALID) == 0) {
+ (void) strlcpy(ucs.ucs_product, "<unknown>",
+ sizeof (ucs.ucs_product));
+ }
+
+ if ((ucs.ucs_status & UCCID_STATUS_F_SERIAL_VALID) == 0) {
+ (void) strlcpy(ucs.ucs_serial, "<unknown>",
+ sizeof (ucs.ucs_serial));
+ }
+
+ (void) printf(" Product: %s\n", ucs.ucs_product);
+ (void) printf(" Serial: %s\n", ucs.ucs_serial);
+ (void) printf(" Slots Present: %u\n", cd->ccd_bMaxSlotIndex + 1);
+ (void) printf(" Maximum Busy Slots: %u\n", cd->ccd_bMaxCCIDBusySlots);
+ (void) printf(" Supported Voltages:\n");
+ ccidadm_print_pairs(cd->ccd_bVoltageSupport, ccidadm_p_voltages);
+ (void) printf(" Supported Protocols:\n");
+ ccidadm_print_pairs(cd->ccd_dwProtocols, ccidadm_p_protocols);
+ nicenum_scale(cd->ccd_dwDefaultClock, 1000, nnbuf,
+ sizeof (nnbuf), NN_DIVISOR_1000 | NN_UNIT_SPACE);
+ (void) printf(" Default Clock: %sHz\n", nnbuf);
+ nicenum_scale(cd->ccd_dwMaximumClock, 1000, nnbuf,
+ sizeof (nnbuf), NN_DIVISOR_1000 | NN_UNIT_SPACE);
+ (void) printf(" Maximum Clock: %sHz\n", nnbuf);
+ (void) printf(" Supported Clock Rates: %u\n",
+ cd->ccd_bNumClockSupported);
+ nicenum_scale(cd->ccd_dwDataRate, 1, nnbuf, sizeof (nnbuf),
+ NN_DIVISOR_1000 | NN_UNIT_SPACE);
+ (void) printf(" Default Data Rate: %sbps\n", nnbuf);
+ nicenum_scale(cd->ccd_dwMaxDataRate, 1, nnbuf, sizeof (nnbuf),
+ NN_DIVISOR_1000 | NN_UNIT_SPACE);
+ (void) printf(" Maximum Data Rate: %sbps\n", nnbuf);
+ (void) printf(" Supported Data Rates: %u\n",
+ cd->ccd_bNumDataRatesSupported);
+ (void) printf(" Maximum IFSD (T=1 only): %u\n", cd->ccd_dwMaxIFSD);
+ if (cd->ccd_dwSyncProtocols != 0) {
+ (void) printf(" Synchronous Protocols Supported:\n");
+ ccidadm_print_pairs(cd->ccd_dwSyncProtocols,
+ ccidadm_p_syncprots);
+ }
+ if (cd->ccd_dwMechanical != 0) {
+ (void) printf(" Mechanical Features:\n");
+ ccidadm_print_pairs(cd->ccd_dwMechanical, ccidadm_p_mechanical);
+ }
+ if (cd->ccd_dwFeatures != 0) {
+ (void) printf(" Device Features:\n");
+ ccidadm_print_pairs(cd->ccd_dwFeatures, ccidadm_p_features);
+ }
+ (void) printf(" Maximum Message Length: %u bytes\n",
+ cd->ccd_dwMaxCCIDMessageLength);
+ if (cd->ccd_dwFeatures & CCID_CLASS_F_EXT_APDU_XCHG) {
+ if (cd->ccd_bClassGetResponse == 0xff) {
+ (void) printf(" Default Get Response Class: echo\n");
+ } else {
+ (void) printf(" Default Get Response Class: %u\n",
+ cd->ccd_bClassGetResponse);
+ }
+ if (cd->ccd_bClassEnvelope == 0xff) {
+ (void) printf(" Default Envelope Class: echo\n");
+ } else {
+ (void) printf(" Default Envelope Class: %u\n",
+ cd->ccd_bClassEnvelope);
+ }
+ }
+ if (cd->ccd_wLcdLayout != 0) {
+ (void) printf(" %2ux%2u LCD present\n",
+ cd->ccd_wLcdLayout >> 8, cd->ccd_wLcdLayout & 0xff);
+ }
+
+ if (cd->ccd_bPinSupport) {
+ (void) printf(" Pin Support:\n");
+ ccidadm_print_pairs(cd->ccd_bPinSupport, ccidadm_p_pin);
+ }
+}
+
+static void
+ccidadm_do_reader(int argc, char *argv[])
+{
+ int i;
+
+ if (argc == 0) {
+ ccidadm_iter(B_TRUE, B_TRUE, ccidadm_reader_print, NULL);
+ return;
+ }
+
+ for (i = 0; i < argc; i++) {
+ int fd;
+
+ if ((fd = ccidadm_open(argv[i], B_TRUE)) < 0) {
+ warn("failed to open %s", argv[i]);
+ errx(EXIT_FAILURE, "valid ccid reader");
+ }
+
+ ccidadm_reader_print(fd, argv[i], NULL);
+ (void) close(fd);
+ if (i + 1 < argc) {
+ (void) printf("\n");
+ }
+ }
+}
+
+static void
+ccidadm_reader_usage(FILE *out)
+{
+ (void) fprintf(out, "\treader\t\t[reader] ...\n");
+}
+
+typedef struct ccidadm_cmdtab {
+ const char *cc_name;
+ void (*cc_op)(int, char *[]);
+ void (*cc_usage)(FILE *);
+} ccidadm_cmdtab_t;
+
+static ccidadm_cmdtab_t ccidadm_cmds[] = {
+ { "list", ccidadm_do_list, ccidadm_list_usage },
+ { "atr", ccidadm_do_atr, ccidadm_atr_usage },
+ { "reader", ccidadm_do_reader, ccidadm_reader_usage },
+ { NULL }
+};
+
+static int
+ccidadm_usage(const char *format, ...)
+{
+ ccidadm_cmdtab_t *tab;
+
+ if (format != NULL) {
+ va_list ap;
+
+ va_start(ap, format);
+ (void) fprintf(stderr, "%s: ", ccidadm_pname);
+ (void) vfprintf(stderr, format, ap);
+ (void) fprintf(stderr, "\n");
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "usage: %s <subcommand> <args> ...\n\n",
+ ccidadm_pname);
+ (void) fprintf(stderr, "Subcommands:\n");
+ for (tab = ccidadm_cmds; tab->cc_name != NULL; tab++) {
+ tab->cc_usage(stderr);
+ }
+
+ return (EXIT_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ ccidadm_cmdtab_t *tab;
+
+ ccidadm_pname = basename(argv[0]);
+ if (argc < 2) {
+ return (ccidadm_usage("missing required subcommand"));
+ }
+
+ for (tab = ccidadm_cmds; tab->cc_name != NULL; tab++) {
+ if (strcmp(argv[1], tab->cc_name) == 0) {
+ argc -= 2;
+ argv += 2;
+ tab->cc_op(argc, argv);
+ return (EXIT_SUCCESS);
+ }
+ }
+
+ return (ccidadm_usage("unknown command: %s", argv[1]));
+}
diff --git a/usr/src/cmd/devfsadm/Makefile.com b/usr/src/cmd/devfsadm/Makefile.com
index 4332220e5f..ec9037409e 100644
--- a/usr/src/cmd/devfsadm/Makefile.com
+++ b/usr/src/cmd/devfsadm/Makefile.com
@@ -185,7 +185,7 @@ $(DEVFSADM_MOD): $(DEVFSADM_OBJ)
$(POST_PROCESS)
SUNW_%.so: %.o $(MAPFILES)
- $(CC) -o $@ $(GSHARED) $(DYNFLAGS) -h $@ $< $(LDLIBS) -lc
+ $(LINK.c) -o $@ $(GSHARED) $(DYNFLAGS) -h $@ $< $(LDLIBS) -lc
$(POST_PROCESS_SO)
%.o: $(COMMON)/%.c
diff --git a/usr/src/cmd/devfsadm/cfg_link.c b/usr/src/cmd/devfsadm/cfg_link.c
index e7229325ac..4415ad55ce 100644
--- a/usr/src/cmd/devfsadm/cfg_link.c
+++ b/usr/src/cmd/devfsadm/cfg_link.c
@@ -20,6 +20,7 @@
*/
/*
+ * Copyright 2019, Joyent, Inc.
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -59,6 +60,7 @@ static int pci_cfg_creat_cb(di_minor_t minor, di_node_t node);
static int ib_cfg_creat_cb(di_minor_t minor, di_node_t node);
static int sata_cfg_creat_cb(di_minor_t minor, di_node_t node);
static int sdcard_cfg_creat_cb(di_minor_t minor, di_node_t node);
+static int ccid_cfg_creat_cb(di_minor_t minor, di_node_t node);
static di_node_t pci_cfg_chassis_node(di_node_t, di_prom_handle_t);
static char *pci_cfg_slotname(di_node_t, di_prom_handle_t, minor_t);
@@ -120,6 +122,9 @@ static devfsadm_create_t cfg_create_cbt[] = {
},
{ "attachment-point", DDI_NT_SDCARD_ATTACHMENT_POINT, NULL,
TYPE_EXACT, ILEVEL_0, sdcard_cfg_creat_cb
+ },
+ { "attachment-point", DDI_NT_CCID_ATTACHMENT_POINT, NULL,
+ TYPE_EXACT, ILEVEL_0, ccid_cfg_creat_cb
}
};
@@ -153,6 +158,9 @@ static devfsadm_remove_t cfg_remove_cbt[] = {
{ "attachment-point", SDCARD_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
ILEVEL_0, devfsadm_rm_all
},
+ { "attachment-point", CCID_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
+ ILEVEL_0, devfsadm_rm_all
+ }
};
DEVFSADM_REMOVE_INIT_V0(cfg_remove_cbt);
@@ -1246,3 +1254,24 @@ serid_printable(uint64_t *seridp)
return (1);
}
+
+/*
+ * Create a link for cfgadm that points back to the normal ccid links in
+ * /dev/ccid.
+ */
+static int
+ccid_cfg_creat_cb(di_minor_t minor, di_node_t node)
+{
+ const char *minor_nm;
+ char cfg_path[MAXPATHLEN];
+
+ if ((minor_nm = di_minor_name(minor)) == NULL) {
+ return (DEVFSADM_CONTINUE);
+ }
+
+ (void) snprintf(cfg_path, sizeof (cfg_path), "%s/ccid%d/%s",
+ CFG_DIRNAME, di_instance(node), minor_nm);
+
+ (void) devfsadm_mklink(cfg_path, node, minor, 0);
+ return (DEVFSADM_CONTINUE);
+}
diff --git a/usr/src/cmd/devfsadm/cfg_link.h b/usr/src/cmd/devfsadm/cfg_link.h
index e2bb24864d..7c3cf13eb8 100644
--- a/usr/src/cmd/devfsadm/cfg_link.h
+++ b/usr/src/cmd/devfsadm/cfg_link.h
@@ -22,6 +22,9 @@
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
/* private devlink info interfaces */
@@ -43,6 +46,7 @@ extern "C" {
#define SDCARD_CFG_LINK_RE "^cfg/sdcard[0-9]+/[0-9]+$"
#define PCI_CFG_PATH_LINK_RE \
"^cfg/(.*(pci[0-9]|pcie[0-9]|Slot[0-9]|\\<pci\\>|\\<pcie\\>).*)$"
+#define CCID_CFG_LINK_RE "^cfg/ccid[0-9]+/slot[0-9]+$"
#define CFG_DIRNAME "cfg"
diff --git a/usr/src/cmd/devfsadm/usb_link.c b/usr/src/cmd/devfsadm/usb_link.c
index ac6a8447fb..80996a1773 100644
--- a/usr/src/cmd/devfsadm/usb_link.c
+++ b/usr/src/cmd/devfsadm/usb_link.c
@@ -21,6 +21,9 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
#include <devfsadm.h>
#include <stdio.h>
@@ -37,6 +40,8 @@ static int usb_process(di_minor_t minor, di_node_t node);
static void ugen_create_link(char *p_path, char *node_name,
di_node_t node, di_minor_t minor);
+static void ccid_create_link(char *p_path, char *node_name,
+ di_node_t node, di_minor_t minor);
/* Rules for creating links */
@@ -81,6 +86,8 @@ static devfsadm_create_t usb_cbt[] = {
ILEVEL_0, usb_process },
{ "usb", DDI_NT_NEXUS, "hwahc", DRV_EXACT|TYPE_EXACT,
ILEVEL_0, usb_process },
+ { "usb", DDI_NT_CCID_ATTACHMENT_POINT, "ccid", DRV_EXACT|TYPE_EXACT,
+ ILEVEL_0, usb_process },
};
/* For debug printing (-V filter) */
@@ -105,6 +112,7 @@ DEVFSADM_CREATE_INIT_V0(usb_cbt);
#define USB_LINK_RE_WHOST "^usb/whost[0-9]+$"
#define USB_LINK_RE_HWARC "^usb/hwarc[0-9]+$"
#define USB_LINK_RE_WUSB_CA "^usb/wusb_ca[0-9]+$"
+#define USB_LINK_RE_CCID "^ccid/ccid[0-9]+/slot[0-9]+$"
/* Rules for removing links */
static devfsadm_remove_t usb_remove_cbt[] = {
@@ -138,7 +146,9 @@ static devfsadm_remove_t usb_remove_cbt[] = {
{ "usb", USB_LINK_RE_HWARC, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
devfsadm_rm_all },
{ "usb", USB_LINK_RE_WUSB_CA, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
- devfsadm_rm_all }
+ devfsadm_rm_all },
+ { "usb", USB_LINK_RE_CCID, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
+ devfsadm_rm_all }
};
/*
@@ -306,6 +316,14 @@ usb_process(di_minor_t minor, di_node_t node)
return (DEVFSADM_CONTINUE);
}
+ if (strcmp(di_minor_nodetype(minor), DDI_NT_CCID_ATTACHMENT_POINT)
+ == 0) {
+ ccid_create_link(p_path, minor_nm, node, minor);
+ free(l_path);
+ free(p_path);
+ return (DEVFSADM_CONTINUE);
+ }
+
/* Figure out which rules to apply */
switch (index) {
case DRIVER_HUBD:
@@ -493,3 +511,19 @@ ugen_create_link(char *p_path, char *node_name,
free(buf);
}
+
+/*
+ * Create a CCID related link.
+ */
+static void
+ccid_create_link(char *p_path, char *minor_nm, di_node_t node, di_minor_t minor)
+{
+ char l_path[MAXPATHLEN];
+
+ (void) snprintf(l_path, sizeof (l_path), "ccid/ccid%d/%s",
+ di_instance(node), minor_nm);
+
+ devfsadm_print(debug_mid, "mklink %s -> %s\n", l_path, p_path);
+
+ (void) devfsadm_mklink(l_path, node, minor, 0);
+}
diff --git a/usr/src/cmd/dumpadm/svc-dumpadm b/usr/src/cmd/dumpadm/svc-dumpadm
index 316e075754..488ba8d54d 100644
--- a/usr/src/cmd/dumpadm/svc-dumpadm
+++ b/usr/src/cmd/dumpadm/svc-dumpadm
@@ -21,6 +21,7 @@
#
#
# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2020 Joyent, Inc.
#
. /lib/svc/share/smf_include.sh
@@ -100,7 +101,7 @@ fi
# how to modify the dump settings.
#
if [ -x /usr/sbin/dumpadm ]; then
- /usr/sbin/dumpadm -u || $SMF_EXIT_ERR_CONFIG
+ /usr/sbin/dumpadm -u || exit $SMF_EXIT_ERR_CONFIG
else
echo "WARNING: /usr/sbin/dumpadm is missing or not executable" >& 2
exit $SMF_EXIT_ERR_CONFIG
@@ -113,6 +114,11 @@ else
exit $SMF_EXIT_ERR_CONFIG
fi
+if [[ -f $DUMPADM_SAVDIR/keyfile ]]; then
+ /usr/sbin/dumpadm -k $DUMPADM_SAVDIR/keyfile || \
+ exit $SMT_EXIT_ERR_CONFIG
+fi
+
#
# If the savecore executable is absent then we're done
#
@@ -147,11 +153,15 @@ if [ "x$DUMPADM_ENABLE" != xno ]; then
mksavedir && /usr/bin/savecore $DUMPADM_SAVDIR &
fi
else
+ keyarg=""
+ [[ -f "$DUMPADM_SAVDIR/keyfile" ]] && \
+ keyarg="-k $DUMPADM_SAVDIR/keyfile"
+
#
# The dump device couldn't have been dedicated before we
# ran dumpadm, so we must execute savecore again.
#
- mksavedir && /usr/bin/savecore $DUMPADM_SAVDIR &
+ mksavedir && /usr/bin/savecore $keyarg $DUMPADM_SAVDIR &
fi
else
#
diff --git a/usr/src/cmd/fs.d/nfs/nfsd/Makefile b/usr/src/cmd/fs.d/nfs/nfsd/Makefile
index 384d6a0c66..d9553ac9a2 100644
--- a/usr/src/cmd/fs.d/nfs/nfsd/Makefile
+++ b/usr/src/cmd/fs.d/nfs/nfsd/Makefile
@@ -46,7 +46,7 @@ CERRWARN += -_gcc=-Wno-extra
SMATCH=off
$(TYPEPROG): $(OBJS)
- $(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
$(POST_PROCESS)
nfs_tbind.o: ../lib/nfs_tbind.c
diff --git a/usr/src/cmd/genmsg/Makefile b/usr/src/cmd/genmsg/Makefile
index aafd07c843..420bfd58d1 100644
--- a/usr/src/cmd/genmsg/Makefile
+++ b/usr/src/cmd/genmsg/Makefile
@@ -69,7 +69,7 @@ all: $(PROG)
install: all $(ROOTPROG)
$(PROG): $(OBJS) $(MAPFILES)
- $(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
$(POST_PROCESS)
$(LEXINTSRCS): $(LEXSRCS)
diff --git a/usr/src/cmd/init/init.c b/usr/src/cmd/init/init.c
index edf58e22bf..b37322aa5c 100644
--- a/usr/src/cmd/init/init.c
+++ b/usr/src/cmd/init/init.c
@@ -23,7 +23,7 @@
* Copyright (c) 2013 Gary Mills
*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -701,8 +701,7 @@ main(int argc, char *argv[])
console(B_FALSE,
"\n\n%s Release %s Version %s %d-bit\r\n",
un.sysname, un.release, un.version, bits);
- console(B_FALSE, "Copyright (c) 2010-2012, "
- "Joyent Inc. All rights reserved.\r\n");
+ console(B_FALSE, "Copyright 2010-2020 Joyent, Inc.\r\n");
}
/*
diff --git a/usr/src/cmd/ksh/builtins/Makefile b/usr/src/cmd/ksh/builtins/Makefile
index 1033c6cdd3..2a3977c94c 100644
--- a/usr/src/cmd/ksh/builtins/Makefile
+++ b/usr/src/cmd/ksh/builtins/Makefile
@@ -49,7 +49,6 @@ ALIASPROG= \
print \
read \
rev \
- sleep \
sum \
tee \
test \
diff --git a/usr/src/cmd/make/Makefile.com b/usr/src/cmd/make/Makefile.com
index dcc587439e..fc76730f2a 100644
--- a/usr/src/cmd/make/Makefile.com
+++ b/usr/src/cmd/make/Makefile.com
@@ -14,6 +14,7 @@
MAKE_INCLUDE= $(SRC)/cmd/make/include
CFLAGS += $(CCVERBOSE)
CPPFLAGS += -I$(MAKE_INCLUDE) $(MAKE_DEFS)
+CCFLAGS += -_gcc4=-std=gnu++0x
# So that it's set even for the libraries we build
TEXT_DOMAIN = SUNW_OST_OSCMD
diff --git a/usr/src/cmd/make/include/mksh/defs.h b/usr/src/cmd/make/include/mksh/defs.h
index 9ad58c4e61..0cf3d837c8 100644
--- a/usr/src/cmd/make/include/mksh/defs.h
+++ b/usr/src/cmd/make/include/mksh/defs.h
@@ -804,6 +804,7 @@ public:
// constructors
iterator() : node(0) {}
iterator(entry *node_) : node(node_) {}
+ iterator(const iterator&) = default;
// dereference operator
Name operator->() const { return node->name; }
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
index d14ce2fbed..628503d179 100644
--- a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
@@ -2394,9 +2394,9 @@ kmt_destroy(mdb_tgt_t *t)
static const mdb_tgt_ops_t kmt_ops = {
kmt_setflags, /* t_setflags */
- (int (*)()) mdb_tgt_notsup, /* t_setcontext */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_setcontext */
kmt_activate, /* t_activate */
- (void (*)()) mdb_tgt_nop, /* t_deactivate */
+ (void (*)())(uintptr_t) mdb_tgt_nop, /* t_deactivate */
kmt_periodic, /* t_periodic */
kmt_destroy, /* t_destroy */
kmt_name, /* t_name */
@@ -2425,25 +2425,25 @@ static const mdb_tgt_ops_t kmt_ops = {
kmt_addr_to_ctf, /* t_addr_to_ctf */
kmt_name_to_ctf, /* t_name_to_ctf */
kmt_status, /* t_status */
- (int (*)()) mdb_tgt_notsup, /* t_run */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_run */
kmt_step, /* t_step */
kmt_step_out, /* t_step_out */
kmt_next, /* t_next */
kmt_continue, /* t_cont */
- (int (*)()) mdb_tgt_notsup, /* t_signal */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_signal */
kmt_add_vbrkpt, /* t_add_vbrkpt */
kmt_add_sbrkpt, /* t_add_sbrkpt */
kmt_add_pwapt, /* t_add_pwapt */
kmt_add_vwapt, /* t_add_vwapt */
kmt_add_iowapt, /* t_add_iowapt */
- (int (*)()) mdb_tgt_null, /* t_add_sysenter */
- (int (*)()) mdb_tgt_null, /* t_add_sysexit */
- (int (*)()) mdb_tgt_null, /* t_add_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysenter */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysexit */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_signal */
kmt_add_trap, /* t_add_fault */
kmt_getareg, /* t_getareg */
kmt_putareg, /* t_putareg */
- (int (*)()) mdb_tgt_nop, /* XXX t_stack_iter */
- (int (*)()) mdb_tgt_notsup /* t_auxv */
+ (int (*)())(uintptr_t) mdb_tgt_nop, /* XXX t_stack_iter */
+ (int (*)())(uintptr_t) mdb_tgt_notsup /* t_auxv */
};
/*
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
index 48b6e1ec93..cae14e419d 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
@@ -65,7 +65,7 @@ libkvm_kb_ops(void)
.kb_awrite = (ssize_t (*)())kvm_awrite,
.kb_pread = (ssize_t (*)())kvm_pread,
.kb_pwrite = (ssize_t (*)())kvm_pwrite,
- .kb_getmregs = (int (*)())mdb_tgt_notsup,
+ .kb_getmregs = (int (*)())(uintptr_t)mdb_tgt_notsup,
.kb_vtop = (uint64_t (*)())kvm_physaddr,
};
return (&ops);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kproc.c b/usr/src/cmd/mdb/common/mdb/mdb_kproc.c
index f09b97db0d..4eeb8ac708 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kproc.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kproc.c
@@ -885,11 +885,11 @@ kp_auxv(mdb_tgt_t *t, const auxv_t **auxvp)
}
static const mdb_tgt_ops_t kproc_ops = {
- (int (*)()) mdb_tgt_notsup, /* t_setflags */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_setflags */
kp_setcontext, /* t_setcontext */
kp_activate, /* t_activate */
kp_deactivate, /* t_deactivate */
- (void (*)()) mdb_tgt_nop, /* t_periodic */
+ (void (*)())(uintptr_t) mdb_tgt_nop, /* t_periodic */
kp_destroy, /* t_destroy */
kp_name, /* t_name */
kp_isa, /* t_isa */
@@ -917,24 +917,24 @@ static const mdb_tgt_ops_t kproc_ops = {
(struct ctf_file *(*)()) mdb_tgt_null, /* t_addr_to_ctf */
(struct ctf_file *(*)()) mdb_tgt_null, /* t_name_to_ctf */
kp_status, /* t_status */
- (int (*)()) mdb_tgt_notsup, /* t_run */
- (int (*)()) mdb_tgt_notsup, /* t_step */
- (int (*)()) mdb_tgt_notsup, /* t_step_out */
- (int (*)()) mdb_tgt_notsup, /* t_next */
- (int (*)()) mdb_tgt_notsup, /* t_cont */
- (int (*)()) mdb_tgt_notsup, /* t_signal */
- (int (*)()) mdb_tgt_null, /* t_add_sbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_vbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_pwapt */
- (int (*)()) mdb_tgt_null, /* t_add_vwapt */
- (int (*)()) mdb_tgt_null, /* t_add_iowapt */
- (int (*)()) mdb_tgt_null, /* t_add_sysenter */
- (int (*)()) mdb_tgt_null, /* t_add_sysexit */
- (int (*)()) mdb_tgt_null, /* t_add_signal */
- (int (*)()) mdb_tgt_null, /* t_add_fault */
- (int (*)()) mdb_tgt_notsup, /* t_getareg XXX */
- (int (*)()) mdb_tgt_notsup, /* t_putareg XXX */
- (int (*)()) mdb_tgt_notsup, /* t_stack_iter XXX */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_run */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_step */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_step_out */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_next */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_cont */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_pwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_iowapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysenter */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysexit */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_fault */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_getareg XXX */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_putareg XXX */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_stack_iter XXX */
kp_auxv /* t_auxv */
};
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
index 2a446d6fc9..27ca238ca9 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
@@ -1496,7 +1496,7 @@ mdb_kvm_tgt_create(mdb_tgt_t *t, int argc, const char *argv[])
goto err;
}
- kt->k_dump_print_content = (void (*)())kt_data_stub;
+ kt->k_dump_print_content = (void (*)())(uintptr_t)kt_data_stub;
kt->k_dump_find_curproc = kt_data_stub;
/*
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_main.c b/usr/src/cmd/mdb/common/mdb/mdb_main.c
index 1077e1b719..e1c2ce5fa4 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_main.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c
@@ -506,8 +506,10 @@ main(int argc, char *argv[], char *envp[])
(void) mdb_signal_sethandler(SIGBUS, flt_handler, NULL);
(void) mdb_signal_sethandler(SIGSEGV, flt_handler, NULL);
- (void) mdb_signal_sethandler(SIGHUP, (mdb_signal_f *)terminate, NULL);
- (void) mdb_signal_sethandler(SIGTERM, (mdb_signal_f *)terminate, NULL);
+ (void) mdb_signal_sethandler(SIGHUP,
+ (mdb_signal_f *)(uintptr_t)terminate, NULL);
+ (void) mdb_signal_sethandler(SIGTERM,
+ (mdb_signal_f *)(uintptr_t)terminate, NULL);
for (mdb.m_rdvers = RD_VERSION; mdb.m_rdvers > 0; mdb.m_rdvers--) {
if (rd_init(mdb.m_rdvers) == RD_OK)
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_proc.c b/usr/src/cmd/mdb/common/mdb/mdb_proc.c
index 2fd564d122..af4a6eac23 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_proc.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_proc.c
@@ -4681,7 +4681,7 @@ pt_auxv(mdb_tgt_t *t, const auxv_t **auxvp)
static const mdb_tgt_ops_t proc_ops = {
pt_setflags, /* t_setflags */
- (int (*)()) mdb_tgt_notsup, /* t_setcontext */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_setcontext */
pt_activate, /* t_activate */
pt_deactivate, /* t_deactivate */
pt_periodic, /* t_periodic */
@@ -4701,7 +4701,7 @@ static const mdb_tgt_ops_t proc_ops = {
pt_fwrite, /* t_fwrite */
(ssize_t (*)()) mdb_tgt_notsup, /* t_ioread */
(ssize_t (*)()) mdb_tgt_notsup, /* t_iowrite */
- (int (*)()) mdb_tgt_notsup, /* t_vtop */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_vtop */
pt_lookup_by_name, /* t_lookup_by_name */
pt_lookup_by_addr, /* t_lookup_by_addr */
pt_symbol_iter, /* t_symbol_iter */
@@ -4720,9 +4720,9 @@ static const mdb_tgt_ops_t proc_ops = {
pt_signal, /* t_signal */
pt_add_vbrkpt, /* t_add_vbrkpt */
pt_add_sbrkpt, /* t_add_sbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_pwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_pwapt */
pt_add_vwapt, /* t_add_vwapt */
- (int (*)()) mdb_tgt_null, /* t_add_iowapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_iowapt */
pt_add_sysenter, /* t_add_sysenter */
pt_add_sysexit, /* t_add_sysexit */
pt_add_signal, /* t_add_signal */
@@ -4848,8 +4848,8 @@ pt_lwp_setfpregs(mdb_tgt_t *t, void *tap, mdb_tgt_tid_t tid,
}
static const pt_ptl_ops_t proc_lwp_ops = {
- (int (*)()) mdb_tgt_nop,
- (void (*)()) mdb_tgt_nop,
+ (int (*)())(uintptr_t) mdb_tgt_nop,
+ (void (*)())(uintptr_t) mdb_tgt_nop,
pt_lwp_tid,
pt_lwp_iter,
pt_lwp_getregs,
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_rawfile.c b/usr/src/cmd/mdb/common/mdb/mdb_rawfile.c
index 529b71a581..d21ad0f38a 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_rawfile.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_rawfile.c
@@ -361,16 +361,16 @@ rf_deactivate(mdb_tgt_t *t)
static const mdb_tgt_ops_t rawfile_ops = {
rf_setflags, /* t_setflags */
- (int (*)()) mdb_tgt_notsup, /* t_setcontext */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_setcontext */
rf_activate, /* t_activate */
rf_deactivate, /* t_deactivate */
- (void (*)()) mdb_tgt_nop, /* t_periodic */
+ (void (*)())(uintptr_t) mdb_tgt_nop, /* t_periodic */
rf_destroy, /* t_destroy */
rf_name, /* t_name */
(const char *(*)()) mdb_conf_isa, /* t_isa */
(const char *(*)()) mdb_conf_platform, /* t_platform */
- (int (*)()) mdb_tgt_notsup, /* t_uname */
- (int (*)()) mdb_tgt_notsup, /* t_dmodel */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_uname */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_dmodel */
rf_aread, /* t_aread */
rf_awrite, /* t_awrite */
rf_vread, /* t_vread */
@@ -381,10 +381,10 @@ static const mdb_tgt_ops_t rawfile_ops = {
rf_fwrite, /* t_fwrite */
(ssize_t (*)()) mdb_tgt_notsup, /* t_ioread */
(ssize_t (*)()) mdb_tgt_notsup, /* t_iowrite */
- (int (*)()) mdb_tgt_notsup, /* t_vtop */
- (int (*)()) mdb_tgt_notsup, /* t_lookup_by_name */
- (int (*)()) mdb_tgt_notsup, /* t_lookup_by_addr */
- (int (*)()) mdb_tgt_notsup, /* t_symbol_iter */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_vtop */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_lookup_by_name */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_lookup_by_addr */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_symbol_iter */
rf_mapping_iter, /* t_mapping_iter */
rf_mapping_iter, /* t_object_iter */
(const mdb_map_t *(*)()) mdb_tgt_null, /* t_addr_to_map */
@@ -392,25 +392,25 @@ static const mdb_tgt_ops_t rawfile_ops = {
(struct ctf_file *(*)()) mdb_tgt_null, /* t_addr_to_ctf */
(struct ctf_file *(*)()) mdb_tgt_null, /* t_name_to_ctf */
rf_status, /* t_status */
- (int (*)()) mdb_tgt_notsup, /* t_run */
- (int (*)()) mdb_tgt_notsup, /* t_step */
- (int (*)()) mdb_tgt_notsup, /* t_step_out */
- (int (*)()) mdb_tgt_notsup, /* t_next */
- (int (*)()) mdb_tgt_notsup, /* t_cont */
- (int (*)()) mdb_tgt_notsup, /* t_signal */
- (int (*)()) mdb_tgt_null, /* t_add_vbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_sbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_pwapt */
- (int (*)()) mdb_tgt_null, /* t_add_vwapt */
- (int (*)()) mdb_tgt_null, /* t_add_iowapt */
- (int (*)()) mdb_tgt_null, /* t_add_sysenter */
- (int (*)()) mdb_tgt_null, /* t_add_sysexit */
- (int (*)()) mdb_tgt_null, /* t_add_signal */
- (int (*)()) mdb_tgt_null, /* t_add_fault */
- (int (*)()) mdb_tgt_notsup, /* t_getareg */
- (int (*)()) mdb_tgt_notsup, /* t_putareg */
- (int (*)()) mdb_tgt_notsup, /* t_stack_iter */
- (int (*)()) mdb_tgt_notsup /* t_auxv */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_run */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_step */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_step_out */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_next */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_cont */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_pwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_iowapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysenter */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysexit */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_fault */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_getareg */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_putareg */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_stack_iter */
+ (int (*)())(uintptr_t) mdb_tgt_notsup /* t_auxv */
};
int
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_value.c b/usr/src/cmd/mdb/common/mdb/mdb_value.c
index c50d971a49..26fd503b29 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_value.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_value.c
@@ -107,17 +107,17 @@ value_write(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr)
}
static const mdb_tgt_ops_t value_ops = {
- (int (*)()) mdb_tgt_notsup, /* t_setflags */
- (int (*)()) mdb_tgt_notsup, /* t_setcontext */
- (void (*)()) mdb_tgt_nop, /* t_activate */
- (void (*)()) mdb_tgt_nop, /* t_deactivate */
- (void (*)()) mdb_tgt_nop, /* t_periodic */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_setflags */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_setcontext */
+ (void (*)())(uintptr_t) mdb_tgt_nop, /* t_activate */
+ (void (*)())(uintptr_t) mdb_tgt_nop, /* t_deactivate */
+ (void (*)())(uintptr_t) mdb_tgt_nop, /* t_periodic */
mdb_value_tgt_destroy, /* t_destroy */
(const char *(*)()) mdb_tgt_null, /* t_name */
(const char *(*)()) mdb_conf_isa, /* t_isa */
(const char *(*)()) mdb_conf_platform, /* t_platform */
- (int (*)()) mdb_tgt_notsup, /* t_uname */
- (int (*)()) mdb_tgt_notsup, /* t_dmodel */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_uname */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_dmodel */
(ssize_t (*)()) mdb_tgt_notsup, /* t_aread */
(ssize_t (*)()) mdb_tgt_notsup, /* t_awrite */
value_read, /* t_vread */
@@ -128,36 +128,36 @@ static const mdb_tgt_ops_t value_ops = {
value_write, /* t_fwrite */
value_read, /* t_ioread */
value_write, /* t_iowrite */
- (int (*)()) mdb_tgt_notsup, /* t_vtop */
- (int (*)()) mdb_tgt_notsup, /* t_lookup_by_name */
- (int (*)()) mdb_tgt_notsup, /* t_lookup_by_addr */
- (int (*)()) mdb_tgt_notsup, /* t_symbol_iter */
- (int (*)()) mdb_tgt_notsup, /* t_mapping_iter */
- (int (*)()) mdb_tgt_notsup, /* t_object_iter */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_vtop */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_lookup_by_name */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_lookup_by_addr */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_symbol_iter */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_mapping_iter */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_object_iter */
(const mdb_map_t *(*)()) mdb_tgt_null, /* t_addr_to_map */
(const mdb_map_t *(*)()) mdb_tgt_null, /* t_name_to_map */
(struct ctf_file *(*)()) mdb_tgt_null, /* t_addr_to_ctf */
(struct ctf_file *(*)()) mdb_tgt_null, /* t_name_to_ctf */
- (int (*)()) mdb_tgt_notsup, /* t_status */
- (int (*)()) mdb_tgt_notsup, /* t_run */
- (int (*)()) mdb_tgt_notsup, /* t_step */
- (int (*)()) mdb_tgt_notsup, /* t_step_out */
- (int (*)()) mdb_tgt_notsup, /* t_next */
- (int (*)()) mdb_tgt_notsup, /* t_cont */
- (int (*)()) mdb_tgt_notsup, /* t_signal */
- (int (*)()) mdb_tgt_null, /* t_add_vbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_sbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_pwapt */
- (int (*)()) mdb_tgt_null, /* t_add_vwapt */
- (int (*)()) mdb_tgt_null, /* t_add_iowapt */
- (int (*)()) mdb_tgt_null, /* t_add_sysenter */
- (int (*)()) mdb_tgt_null, /* t_add_sysexit */
- (int (*)()) mdb_tgt_null, /* t_add_signal */
- (int (*)()) mdb_tgt_null, /* t_add_fault */
- (int (*)()) mdb_tgt_notsup, /* t_getareg */
- (int (*)()) mdb_tgt_notsup, /* t_putareg */
- (int (*)()) mdb_tgt_nop, /* t_stack_iter */
- (int (*)()) mdb_tgt_notsup /* t_auxv */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_status */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_run */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_step */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_step_out */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_next */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_cont */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_pwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_iowapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysenter */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysexit */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_fault */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_getareg */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_putareg */
+ (int (*)())(uintptr_t) mdb_tgt_nop, /* t_stack_iter */
+ (int (*)())(uintptr_t) mdb_tgt_notsup /* t_auxv */
};
int
diff --git a/usr/src/cmd/mdb/common/modules/crypto/impl.c b/usr/src/cmd/mdb/common/modules/crypto/impl.c
index 0d6cc436a1..058548fe0d 100644
--- a/usr/src/cmd/mdb/common/modules/crypto/impl.c
+++ b/usr/src/cmd/mdb/common/modules/crypto/impl.c
@@ -410,7 +410,7 @@ soft_conf_walk_init(mdb_walk_state_t *wsp)
}
wsp->walk_addr = (uintptr_t)soft;
wsp->walk_data = mdb_alloc(sizeof (kcf_soft_conf_entry_t), UM_SLEEP);
- wsp->walk_callback = (mdb_walk_cb_t)prt_soft_conf_entry;
+ wsp->walk_callback = (mdb_walk_cb_t)(uintptr_t)prt_soft_conf_entry;
return (WALK_NEXT);
}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/memory.c b/usr/src/cmd/mdb/common/modules/genunix/memory.c
index c645b04a25..d5ffa1537a 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/memory.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/memory.c
@@ -565,7 +565,7 @@ memstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
stats.ms_unused_vp = (struct vnode *)(uintptr_t)sym.st_value;
/* walk all pages, collect statistics */
- if (mdb_walk("allpages", (mdb_walk_cb_t)memstat_callback,
+ if (mdb_walk("allpages", (mdb_walk_cb_t)(uintptr_t)memstat_callback,
&stats) == -1) {
mdb_warn("can't walk memseg");
return (DCMD_ERR);
diff --git a/usr/src/cmd/mdb/common/modules/genunix/tsd.c b/usr/src/cmd/mdb/common/modules/genunix/tsd.c
index 829244eab7..5bd30496be 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/tsd.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/tsd.c
@@ -143,7 +143,8 @@ tsdtot(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if (addr == 0 || argc != 0)
return (DCMD_USAGE);
- if (mdb_walk("thread", (mdb_walk_cb_t)tsdthr_match, (void *)addr) == -1)
+ if (mdb_walk("thread", (mdb_walk_cb_t)(uintptr_t)tsdthr_match,
+ (void *)addr) == -1)
return (DCMD_ERR);
return (DCMD_OK);
}
diff --git a/usr/src/cmd/mdb/common/modules/ufs/ufs.c b/usr/src/cmd/mdb/common/modules/ufs/ufs.c
index 422048adc2..b81da1e97d 100644
--- a/usr/src/cmd/mdb/common/modules/ufs/ufs.c
+++ b/usr/src/cmd/mdb/common/modules/ufs/ufs.c
@@ -196,7 +196,8 @@ inode_cache(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
"ADDR", "INUMBER", "DEVICE", "CHAIN");
}
- if (mdb_walk("inode_cache", (mdb_walk_cb_t)inode_cache_cb, &id) == -1) {
+ if (mdb_walk("inode_cache", (mdb_walk_cb_t)(uintptr_t)inode_cache_cb,
+ &id) == -1) {
mdb_warn("can't walk inode cache");
return (DCMD_ERR);
}
diff --git a/usr/src/cmd/mdb/common/modules/ufs/ufs_log.c b/usr/src/cmd/mdb/common/modules/ufs/ufs_log.c
index fc3d97a736..f8de1d811c 100644
--- a/usr/src/cmd/mdb/common/modules/ufs/ufs_log.c
+++ b/usr/src/cmd/mdb/common/modules/ufs/ufs_log.c
@@ -214,7 +214,8 @@ mapstats_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
msp = mdb_zalloc(sizeof (mapstats_t), UM_SLEEP | UM_GC);
msp->transdiff = FALSE;
- if (mdb_pwalk("ufslogmap", (mdb_walk_cb_t)mapadd, msp, addr) == -1) {
+ if (mdb_pwalk("ufslogmap", (mdb_walk_cb_t)(uintptr_t)mapadd,
+ msp, addr) == -1) {
mdb_warn("can't walk ufslogmap for stats");
return (DCMD_ERR);
}
diff --git a/usr/src/cmd/mdb/common/modules/usba/prtusb.c b/usr/src/cmd/mdb/common/modules/usba/prtusb.c
index 9f6ffc4177..aed8bf8001 100644
--- a/usr/src/cmd/mdb/common/modules/usba/prtusb.c
+++ b/usr/src/cmd/mdb/common/modules/usba/prtusb.c
@@ -22,12 +22,12 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2016 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/mdb_modapi.h>
-
+#include <sys/sysmacros.h>
#include <sys/usb/usba.h>
#include <sys/usb/usba/usba_types.h>
@@ -484,6 +484,32 @@ static usb_descr_item_t usb_vs_format_dv_descr[] = {
};
static uint_t usb_vs_format_dv_item = 6;
+static usb_descr_item_t usb_ccid_descr[] = {
+ {1, "bLength"},
+ {1, "bDescriptorType"},
+ {2, "bcdCCID"},
+ {1, "bMaxSlotIndex"},
+ {1, "bVoltageSupport"},
+ {4, "dwProtocols"},
+ {4, "dwDefaultClock"},
+ {4, "dwMaximumClock"},
+ {1, "bNumClockSupported"},
+ {4, "dwDataRate"},
+ {4, "dwMaxDataRate"},
+ {1, "bNumDataRatesSupported"},
+ {4, "dwMaxIFSD"},
+ {4, "dwSyncProtocols"},
+ {4, "dwMechanical"},
+ {4, "dwFeatures"},
+ {4, "dwMaxCCIDMessageLength"},
+ {1, "bClassGetResponse"},
+ {1, "bClassEnvelope"},
+ {2, "wLcdLayout"},
+ {1, "bPinSupport"},
+ {1, "bMaxCCIDBusySlots"}
+};
+static uint_t usb_ccid_item = ARRAY_SIZE(usb_ccid_descr);
+
/* ****************************************************************** */
@@ -1131,6 +1157,11 @@ prt_usb_desc(uintptr_t usb_cfg, uint_t cfg_len)
mdb_printf("WA Descriptor\n");
print_descr(paddr, nlen, usb_wa_descr,
usb_wa_item);
+ } else if (usb_if.bInterfaceClass == USB_CLASS_CCID &&
+ usb_if.bInterfaceSubClass == 0x0) {
+ mdb_printf("CCID Descriptor\n");
+ print_descr(paddr, nlen, usb_ccid_descr,
+ usb_ccid_item);
} else {
mdb_printf("HID Descriptor\n");
print_descr(paddr, nlen, usb_hid_descr,
diff --git a/usr/src/cmd/mdb/intel/mdb/kvm_amd64dep.c b/usr/src/cmd/mdb/intel/mdb/kvm_amd64dep.c
index 985da328dc..0382c7e0fb 100644
--- a/usr/src/cmd/mdb/intel/mdb/kvm_amd64dep.c
+++ b/usr/src/cmd/mdb/intel/mdb/kvm_amd64dep.c
@@ -108,7 +108,7 @@ const mdb_tgt_ops_t kt_amd64_ops = {
kt_setcontext, /* t_setcontext */
kt_activate, /* t_activate */
kt_deactivate, /* t_deactivate */
- (void (*)()) mdb_tgt_nop, /* t_periodic */
+ (void (*)())(uintptr_t) mdb_tgt_nop, /* t_periodic */
kt_destroy, /* t_destroy */
kt_name, /* t_name */
(const char *(*)()) mdb_conf_isa, /* t_isa */
@@ -136,25 +136,25 @@ const mdb_tgt_ops_t kt_amd64_ops = {
kt_addr_to_ctf, /* t_addr_to_ctf */
kt_name_to_ctf, /* t_name_to_ctf */
kt_status, /* t_status */
- (int (*)()) mdb_tgt_notsup, /* t_run */
- (int (*)()) mdb_tgt_notsup, /* t_step */
- (int (*)()) mdb_tgt_notsup, /* t_step_out */
- (int (*)()) mdb_tgt_notsup, /* t_next */
- (int (*)()) mdb_tgt_notsup, /* t_cont */
- (int (*)()) mdb_tgt_notsup, /* t_signal */
- (int (*)()) mdb_tgt_null, /* t_add_vbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_sbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_pwapt */
- (int (*)()) mdb_tgt_null, /* t_add_vwapt */
- (int (*)()) mdb_tgt_null, /* t_add_iowapt */
- (int (*)()) mdb_tgt_null, /* t_add_sysenter */
- (int (*)()) mdb_tgt_null, /* t_add_sysexit */
- (int (*)()) mdb_tgt_null, /* t_add_signal */
- (int (*)()) mdb_tgt_null, /* t_add_fault */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_run */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_step */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_step_out */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_next */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_cont */
+ (int (*)())(uintptr_t) mdb_tgt_notsup, /* t_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_pwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_iowapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysenter */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysexit */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_fault */
kt_getareg, /* t_getareg */
kt_putareg, /* t_putareg */
mdb_amd64_kvm_stack_iter, /* t_stack_iter */
- (int (*)()) mdb_tgt_notsup /* t_auxv */
+ (int (*)())(uintptr_t) mdb_tgt_notsup /* t_auxv */
};
void
diff --git a/usr/src/cmd/mdb/intel/mdb/kvm_ia32dep.c b/usr/src/cmd/mdb/intel/mdb/kvm_ia32dep.c
index 7cd9195f29..7a6ecaeb6b 100644
--- a/usr/src/cmd/mdb/intel/mdb/kvm_ia32dep.c
+++ b/usr/src/cmd/mdb/intel/mdb/kvm_ia32dep.c
@@ -108,7 +108,7 @@ const mdb_tgt_ops_t kt_ia32_ops = {
kt_setcontext, /* t_setcontext */
kt_activate, /* t_activate */
kt_deactivate, /* t_deactivate */
- (void (*)()) mdb_tgt_nop, /* t_periodic */
+ (void (*)())(uintptr_t) mdb_tgt_nop, /* t_periodic */
kt_destroy, /* t_destroy */
kt_name, /* t_name */
(const char *(*)()) mdb_conf_isa, /* t_isa */
@@ -142,15 +142,15 @@ const mdb_tgt_ops_t kt_ia32_ops = {
(int (*)()) mdb_tgt_notsup, /* t_next */
(int (*)()) mdb_tgt_notsup, /* t_cont */
(int (*)()) mdb_tgt_notsup, /* t_signal */
- (int (*)()) mdb_tgt_null, /* t_add_vbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_sbrkpt */
- (int (*)()) mdb_tgt_null, /* t_add_pwapt */
- (int (*)()) mdb_tgt_null, /* t_add_vwapt */
- (int (*)()) mdb_tgt_null, /* t_add_iowapt */
- (int (*)()) mdb_tgt_null, /* t_add_sysenter */
- (int (*)()) mdb_tgt_null, /* t_add_sysexit */
- (int (*)()) mdb_tgt_null, /* t_add_signal */
- (int (*)()) mdb_tgt_null, /* t_add_fault */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sbrkpt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_pwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_vwapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_iowapt */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysenter */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_sysexit */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_signal */
+ (int (*)())(uintptr_t) mdb_tgt_null, /* t_add_fault */
kt_getareg, /* t_getareg */
kt_putareg, /* t_putareg */
mdb_ia32_kvm_stack_iter, /* t_stack_iter */
diff --git a/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c b/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
index 798a2c89c0..c74c5f85a7 100644
--- a/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
+++ b/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
@@ -37,7 +37,6 @@
#include <limits.h>
#include <stdlib.h>
#include <assert.h>
-#include <alloca.h>
#include <unistd.h>
#include <stropts.h>
#include <syslog.h>
@@ -668,7 +667,7 @@ add_string_list_prop(picl_nodehdl_t nodeh, char *name, char *strlist,
if (err != PICL_SUCCESS)
return (err);
- proprow = alloca(sizeof (picl_prophdl_t) * nrows);
+ proprow = calloc(nrows, sizeof (picl_prophdl_t));
if (proprow == NULL) {
(void) ptree_destroy_prop(proph);
return (PICL_FAILURE);
@@ -696,10 +695,10 @@ add_string_list_prop(picl_nodehdl_t nodeh, char *name, char *strlist,
(void) ptree_destroy_prop(proprow[i]);
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
- return (err);
}
- return (PICL_SUCCESS);
+ free(proprow);
+ return (err);
}
/*
@@ -714,6 +713,7 @@ compare_string_propval(picl_nodehdl_t nodeh, const char *pname,
int len;
ptree_propinfo_t pinfo;
picl_prophdl_t proph;
+ int rv;
err = ptree_get_prop_by_name(nodeh, pname, &proph);
if (err != PICL_SUCCESS) /* prop doesn't exist */
@@ -725,15 +725,18 @@ compare_string_propval(picl_nodehdl_t nodeh, const char *pname,
len = strlen(pval) + 1;
- pvalbuf = alloca(len);
+ pvalbuf = malloc(len);
if (pvalbuf == NULL)
return (0);
err = ptree_get_propval(proph, pvalbuf, len);
if ((err == PICL_SUCCESS) && (strcmp(pvalbuf, pval) == 0))
- return (1); /* prop match */
+ rv = 1; /* prop match */
+ else
+ rv = 0;
- return (0);
+ free(pvalbuf);
+ return (rv);
}
/*
@@ -846,14 +849,19 @@ process_charstring_data(picl_nodehdl_t nodeh, char *pname, unsigned char *pdata,
* no null terminator
*/
if (pdata[retval - 1] != '\0') {
- strdat = alloca(retval + 1);
- (void) memcpy(strdat, pdata, retval);
- strdat[retval] = '\0';
- retval++;
+ strdat = malloc(retval + 1);
+ if (strdat != NULL) {
+ (void) memcpy(strdat, pdata, retval);
+ strdat[retval] = '\0';
+ retval++;
+ }
} else {
- strdat = alloca(retval);
- (void) memcpy(strdat, pdata, retval);
+ strdat = malloc(retval);
+ if (strdat != NULL)
+ (void) memcpy(strdat, pdata, retval);
}
+ if (strdat == NULL)
+ return (PICL_FAILURE);
/*
* If it's a string list, create a table prop
@@ -862,18 +870,24 @@ process_charstring_data(picl_nodehdl_t nodeh, char *pname, unsigned char *pdata,
if (strcount > 1) {
err = add_string_list_prop(nodeh, pname,
strdat, strcount);
- if (err != PICL_SUCCESS)
+ if (err != PICL_SUCCESS) {
+ free(strdat);
return (err);
+ }
} else {
err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ,
strlen(strdat) + 1, pname, NULL,
NULL);
- if (err != PICL_SUCCESS)
+ if (err != PICL_SUCCESS) {
+ free(strdat);
return (err);
+ }
(void) ptree_create_and_add_prop(nodeh, &propinfo,
strdat, NULL);
}
+
+ free(strdat);
return (PICL_SUCCESS);
}
@@ -2959,14 +2973,14 @@ get_first_reg_word(picl_nodehdl_t nodeh, uint32_t *regval)
return (err);
if (pinfo.piclinfo.size < sizeof (uint32_t)) /* too small */
return (PICL_FAILURE);
- regbuf = alloca(pinfo.piclinfo.size);
+ regbuf = malloc(pinfo.piclinfo.size);
if (regbuf == NULL)
return (PICL_FAILURE);
err = ptree_get_propval(regh, regbuf, pinfo.piclinfo.size);
- if (err != PICL_SUCCESS)
- return (err);
- *regval = *regbuf; /* get first 32-bit value */
- return (PICL_SUCCESS);
+ if (err == PICL_SUCCESS)
+ *regval = *regbuf; /* get first 32-bit value */
+ free(regbuf);
+ return (err);
}
/*
@@ -3324,7 +3338,7 @@ add_unitaddr_prop(picl_nodehdl_t nodeh, unitaddr_map_t *uamap, uint_t addrcells)
return (PICL_FAILURE);
regproplen = pinfo.piclinfo.size;
- regbuf = alloca(regproplen);
+ regbuf = malloc(regproplen);
if (regbuf == NULL)
return (PICL_FAILURE);
@@ -3333,6 +3347,7 @@ add_unitaddr_prop(picl_nodehdl_t nodeh, unitaddr_map_t *uamap, uint_t addrcells)
(uamap->addrcellcnt && uamap->addrcellcnt != addrcells) ||
(uamap->func)(unitaddr, sizeof (unitaddr), regbuf,
addrcells) != 0) {
+ free(regbuf);
return (PICL_FAILURE);
}
@@ -3342,6 +3357,7 @@ add_unitaddr_prop(picl_nodehdl_t nodeh, unitaddr_map_t *uamap, uint_t addrcells)
if (err == PICL_SUCCESS)
err = ptree_create_and_add_prop(nodeh, &pinfo, unitaddr, NULL);
+ free(regbuf);
return (err);
}
@@ -3374,7 +3390,7 @@ get_unitaddr(picl_nodehdl_t parh, picl_nodehdl_t nodeh, char *unitaddr,
return (PICL_FAILURE);
regproplen = pinfo.piclinfo.size;
- regbuf = alloca(regproplen);
+ regbuf = malloc(regproplen);
if (regbuf == NULL)
return (PICL_FAILURE);
@@ -3382,8 +3398,10 @@ get_unitaddr(picl_nodehdl_t parh, picl_nodehdl_t nodeh, char *unitaddr,
if (err != PICL_SUCCESS || uamap->func == NULL ||
(uamap->addrcellcnt && uamap->addrcellcnt != addrcells) ||
(uamap->func)(unitaddr, ualen, regbuf, addrcells) != 0) {
+ free(regbuf);
return (PICL_FAILURE);
}
+ free(regbuf);
return (PICL_SUCCESS);
}
@@ -3465,13 +3483,15 @@ update_memory_size_prop(picl_nodehdl_t plafh)
if (err != PICL_SUCCESS)
return (err);
- regbuf = alloca(pinfo.piclinfo.size);
+ regbuf = malloc(pinfo.piclinfo.size);
if (regbuf == NULL)
return (PICL_FAILURE);
err = ptree_get_propval(proph, regbuf, pinfo.piclinfo.size);
- if (err != PICL_SUCCESS)
+ if (err != PICL_SUCCESS) {
+ free(regbuf);
return (err);
+ }
mspecs = (memspecs_t *)regbuf;
nspecs = pinfo.piclinfo.size / sizeof (memspecs_t);
@@ -3483,6 +3503,7 @@ update_memory_size_prop(picl_nodehdl_t plafh)
err = ptree_get_prop_by_name(memh, PICL_PROP_SIZE, &proph);
if (err == PICL_SUCCESS) {
err = ptree_update_propval(proph, &memsize, sizeof (memsize));
+ free(regbuf);
return (err);
}
@@ -3493,6 +3514,7 @@ update_memory_size_prop(picl_nodehdl_t plafh)
PICL_PTYPE_UNSIGNED_INT, PICL_READ, sizeof (memsize),
PICL_PROP_SIZE, NULL, NULL);
err = ptree_create_and_add_prop(memh, &pinfo, &memsize, NULL);
+ free(regbuf);
return (err);
}
diff --git a/usr/src/cmd/power/Makefile b/usr/src/cmd/power/Makefile
index 77c3699bb9..0b77a62513 100644
--- a/usr/src/cmd/power/Makefile
+++ b/usr/src/cmd/power/Makefile
@@ -27,14 +27,14 @@
DAEMON_SRCS = powerd.c sysstat.c
DAEMON_OBJS = $(DAEMON_SRCS:%.c=%.o)
DAEMON = powerd
-PMCFG_SRCS = conf.c parse.c handlers.c
+PMCFG_SRCS = conf.c parse.c handlers.c
PMCFG_OBJS = $(PMCFG_SRCS:%.c=%.o)
-PMCFG = pmconfig
-SUSPEND_SRCS = sys-suspend.c pm_pam_conv.c
+PMCFG = pmconfig
+SUSPEND_SRCS = sys-suspend.c pm_pam_conv.c
SUSPEND_OBJS = $(SUSPEND_SRCS:%.c=%.o)
-SUSPEND = sys-suspend
-SRCS = $(DAEMON_SRCS) $(PMCFG_SRCS) $(SUSPEND_SRCS)
-OBJS = $(SRCS:%.c=%.o)
+SUSPEND = sys-suspend
+SRCS = $(DAEMON_SRCS) $(PMCFG_SRCS) $(SUSPEND_SRCS)
+OBJS = $(SRCS:%.c=%.o)
PROG = $(DAEMON) $(PMCFG) $(SUSPEND)
POWERCONF= power.conf
ETCFILES = $(POWERCONF)
@@ -99,7 +99,7 @@ $(DAEMON_OBJS): $(DAEMON_SRCS)
$(PROCESS_COMMENT) $@
$(DAEMON): $(DAEMON_OBJS)
- $(CC) -o $@ $(DAEMON_OBJS) $(LDFLAGS) $(DAEMON_LDLIBS)
+ $(LINK.c) -o $@ $(DAEMON_OBJS) $(DAEMON_LDLIBS)
$(POST_PROCESS)
$(PMCFG_OBJS): pmconfig.h
diff --git a/usr/src/cmd/praudit/Makefile b/usr/src/cmd/praudit/Makefile
index 9ae3e01a8b..0749cada9a 100644
--- a/usr/src/cmd/praudit/Makefile
+++ b/usr/src/cmd/praudit/Makefile
@@ -56,7 +56,7 @@ all: $(PROG)
install: all $(ROOTUSRSBINPROG)
$(PROG): $(OBJS)
- $(CC) -o $(PROG) $(OBJS) $(LDFLAGS) $(LDLIBS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
$(POST_PROCESS)
adt_xlate.o: $(XLATEDIR)/adt_xlate.c
diff --git a/usr/src/cmd/sleep/Makefile b/usr/src/cmd/sleep/Makefile
new file mode 100644
index 0000000000..556e4b60ee
--- /dev/null
+++ b/usr/src/cmd/sleep/Makefile
@@ -0,0 +1,29 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Robert Mustacchi
+#
+
+PROG = sleep
+
+include ../Makefile.cmd
+
+CSTD = $(CSTD_GNU99)
+LDLIBS += -lm
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/sleep/sleep.c b/usr/src/cmd/sleep/sleep.c
new file mode 100644
index 0000000000..bd1420a69b
--- /dev/null
+++ b/usr/src/cmd/sleep/sleep.c
@@ -0,0 +1,168 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Robert Mustacchi
+ */
+
+#include <locale.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <math.h>
+#include <limits.h>
+#include <time.h>
+#include <libintl.h>
+
+/*
+ * This implements the sleep(1) command. It allows for a number of extensions
+ * that match both the GNU implementation and parts of what ksh93 used to
+ * provide. Mainly:
+ *
+ * o Fractional seconds
+ * o Suffixes that change the amount of time
+ */
+
+typedef struct {
+ char sm_char;
+ uint64_t sm_adj;
+} sleep_map_t;
+
+static const sleep_map_t sleep_map[] = {
+ { 's', 1 },
+ { 'm', 60 },
+ { 'h', 60 * 60 },
+ { 'd', 60 * 60 * 24 },
+ { 'w', 60 * 60 * 24 * 7 },
+ { 'y', 60 * 60 * 24 * 365 },
+ { '\0', 0 }
+};
+
+static void
+sleep_sigalrm(int sig)
+{
+ /*
+ * Note, the normal exit(2) function is not Async-Signal-Safe.
+ */
+ _exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ long double d, sec, frac;
+ char *eptr;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ (void) signal(SIGALRM, sleep_sigalrm);
+
+ while ((c = getopt(argc, argv, ":")) != -1) {
+ switch (c) {
+ case '?':
+ warnx(gettext("illegal option -- %c"), optopt);
+ (void) fprintf(stderr,
+ gettext("Usage: sleep time[suffix]\n"));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ warnx(gettext("only one operand is supported"));
+ (void) fprintf(stderr, gettext("Usage: sleep time[suffix]\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ errno = 0;
+ d = strtold(argv[0], &eptr);
+ if (errno != 0 || (eptr[0] != '\0' && eptr[1] != '\0') ||
+ eptr == argv[0] || d == NAN) {
+ errx(EXIT_FAILURE, gettext("failed to parse time '%s'"),
+ argv[0]);
+ }
+
+ if (d < 0.0) {
+ errx(EXIT_FAILURE,
+ gettext("time interval '%s', cannot be negative"), argv[0]);
+ }
+
+ if (eptr[0] != '\0') {
+ int i;
+ for (i = 0; sleep_map[i].sm_char != '\0'; i++) {
+ if (sleep_map[i].sm_char == eptr[0]) {
+ d *= sleep_map[i].sm_adj;
+ break;
+ }
+ }
+
+ if (sleep_map[i].sm_char == '\0') {
+ errx(EXIT_FAILURE, gettext("failed to parse time %s"),
+ argv[0]);
+ }
+ }
+
+ /*
+ * If we have no time, then we're done. Short circuit.
+ */
+ if (d == 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ /*
+ * Split this apart into the fractional and seconds parts to make it
+ * easier to work with.
+ */
+ frac = modfl(d, &sec);
+
+ /*
+ * We may have a rather large double value. Chop it up in units of
+ * INT_MAX.
+ */
+ while (sec > 0 || frac != 0) {
+ struct timespec ts;
+
+ if (frac != 0) {
+ frac *= NANOSEC;
+ ts.tv_nsec = (long)frac;
+ frac = 0;
+ } else {
+ ts.tv_nsec = 0;
+ }
+
+ /*
+ * We have a floating point number of fractional seconds. We
+ * need to convert that to nanoseconds.
+ */
+ if (sec > (float)INT_MAX) {
+ ts.tv_sec = INT_MAX;
+ } else {
+ ts.tv_sec = (time_t)sec;
+ }
+ sec -= ts.tv_sec;
+
+ if (nanosleep(&ts, NULL) != 0) {
+ err(EXIT_FAILURE, gettext("nanosleep failed"));
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/smbios/Makefile b/usr/src/cmd/smbios/Makefile
index ead08c39ac..391fe3e53f 100644
--- a/usr/src/cmd/smbios/Makefile
+++ b/usr/src/cmd/smbios/Makefile
@@ -52,6 +52,4 @@ clean:
install: $(ROOTUSRSBINPROG)
-lint: lint_SRCS
-
include ../Makefile.targ
diff --git a/usr/src/cmd/smbios/smbios.c b/usr/src/cmd/smbios/smbios.c
index a07a42e45b..703c278132 100644
--- a/usr/src/cmd/smbios/smbios.c
+++ b/usr/src/cmd/smbios/smbios.c
@@ -187,6 +187,21 @@ jedec_print(FILE *fp, const char *desc, uint_t id)
}
}
+/*
+ * Print a 128-bit data as a series of 16 hex digits.
+ */
+static void
+u128_print(FILE *fp, const char *desc, const uint8_t *data)
+{
+ uint_t i;
+
+ oprintf(fp, "%s: ", desc);
+ for (i = 0; i < 16; i++) {
+ oprintf(fp, " %02x", data[i]);
+ }
+ oprintf(fp, "\n");
+}
+
static int
check_oem(smbios_hdl_t *shp)
{
@@ -505,10 +520,6 @@ print_processor(smbios_hdl_t *shp, id_t id, FILE *fp)
desc_printf(smbios_processor_family_desc(p.smbp_family),
fp, " Family: %u", p.smbp_family);
- if (p.smbp_family2 != 0)
- desc_printf(smbios_processor_family_desc(p.smbp_family2),
- fp, " Family Ext: %u", p.smbp_family2);
-
oprintf(fp, " CPUID: 0x%llx\n", (u_longlong_t)p.smbp_cpuid);
desc_printf(smbios_processor_type_desc(p.smbp_type),
@@ -544,34 +555,19 @@ print_processor(smbios_hdl_t *shp, id_t id, FILE *fp)
}
if (p.smbp_corecount != 0) {
- if (p.smbp_corecount != 0xff || p.smbp_corecount2 == 0)
- oprintf(fp, " Core Count: %u\n", p.smbp_corecount);
- else
- oprintf(fp, " Core Count: %u\n", p.smbp_corecount2);
+ oprintf(fp, " Core Count: %u\n", p.smbp_corecount);
} else {
oprintf(fp, " Core Count: Unknown\n");
}
if (p.smbp_coresenabled != 0) {
- if (p.smbp_coresenabled != 0xff || p.smbp_coresenabled2 == 0) {
- oprintf(fp, " Cores Enabled: %u\n",
- p.smbp_coresenabled);
- } else {
- oprintf(fp, " Cores Enabled: %u\n",
- p.smbp_coresenabled2);
- }
+ oprintf(fp, " Cores Enabled: %u\n", p.smbp_coresenabled);
} else {
oprintf(fp, " Cores Enabled: Unknown\n");
}
if (p.smbp_threadcount != 0) {
- if (p.smbp_threadcount != 0xff || p.smbp_threadcount2 == 0) {
- oprintf(fp, " Thread Count: %u\n",
- p.smbp_threadcount);
- } else {
- oprintf(fp, " Thread Count: %u\n",
- p.smbp_threadcount2);
- }
+ oprintf(fp, " Thread Count: %u\n", p.smbp_threadcount);
} else {
oprintf(fp, " Thread Count: Unknown\n");
}
@@ -975,15 +971,18 @@ print_memdevice(smbios_hdl_t *shp, id_t id, FILE *fp)
flag_printf(fp, "Flags", md.smbmd_flags, sizeof (md.smbmd_flags) * NBBY,
smbios_memdevice_flag_name, smbios_memdevice_flag_desc);
- if (md.smbmd_speed != 0)
- oprintf(fp, " Speed: %u MT/s\n", md.smbmd_speed);
- else
+ if (md.smbmd_extspeed != 0) {
+ oprintf(fp, " Speed: %" PRIu64 " MT/s\n", md.smbmd_extspeed);
+ } else {
oprintf(fp, " Speed: Unknown\n");
+ }
- if (md.smbmd_clkspeed != 0)
- oprintf(fp, " Configured Speed: %u MT/s\n", md.smbmd_clkspeed);
- else
+ if (md.smbmd_extclkspeed != 0) {
+ oprintf(fp, " Configured Speed: %" PRIu64 " MT/s\n",
+ md.smbmd_extclkspeed);
+ } else {
oprintf(fp, " Configured Speed: Unknown\n");
+ }
oprintf(fp, " Device Locator: %s\n", md.smbmd_dloc);
oprintf(fp, " Bank Locator: %s\n", md.smbmd_bloc);
@@ -1423,6 +1422,88 @@ print_powersup(smbios_hdl_t *shp, id_t id, FILE *fp)
}
static void
+print_processor_info_riscv(smbios_hdl_t *shp, id_t id, FILE *fp)
+{
+ smbios_processor_info_riscv_t rv;
+
+ if (smbios_info_processor_riscv(shp, id, &rv) != 0) {
+ smbios_warn(shp, "failed to read RISC-V specific processor "
+ "information");
+ return;
+ }
+
+ if (rv.smbpirv_boothart != 0) {
+ oprintf(fp, " Boot Hart\n");
+ }
+ u128_print(fp, " Hart ID", rv.smbpirv_hartid);
+ u128_print(fp, " Vendor ID", rv.smbpirv_vendid);
+ u128_print(fp, " Architecture ID", rv.smbpirv_archid);
+ u128_print(fp, " Implementation ID", rv.smbpirv_machid);
+ flag64_printf(fp, " ISA", rv.smbpirv_isa,
+ sizeof (rv.smbpirv_isa) * NBBY, smbios_riscv_isa_name,
+ smbios_riscv_isa_desc);
+ flag_printf(fp, " Privilege Levels", rv.smbpirv_privlvl,
+ sizeof (rv.smbpirv_privlvl) * NBBY, smbios_riscv_priv_name,
+ smbios_riscv_priv_desc);
+ u128_print(fp, " Machine Exception Trap Delegation",
+ rv.smbpirv_metdi);
+ u128_print(fp, " Machine Interrupt Trap Delegation",
+ rv.smbpirv_mitdi);
+ desc_printf(smbios_riscv_width_desc(rv.smbpirv_xlen),
+ fp, " Register Width: 0x%x", rv.smbpirv_xlen);
+ desc_printf(smbios_riscv_width_desc(rv.smbpirv_mxlen),
+ fp, " M-Mode Register Width: 0x%x", rv.smbpirv_mxlen);
+ desc_printf(smbios_riscv_width_desc(rv.smbpirv_sxlen),
+ fp, " S-Mode Register Width: 0x%x", rv.smbpirv_sxlen);
+ desc_printf(smbios_riscv_width_desc(rv.smbpirv_uxlen),
+ fp, " U-Mode Register Width: 0x%x", rv.smbpirv_uxlen);
+}
+
+static void
+print_processor_info(smbios_hdl_t *shp, id_t id, FILE *fp)
+{
+ smbios_processor_info_t p;
+
+ if (smbios_info_processor_info(shp, id, &p) != 0) {
+ smbios_warn(shp, "failed to read processor additional "
+ "information");
+ return;
+ }
+
+ id_printf(fp, " Processor Handle: ", p.smbpi_processor);
+ desc_printf(smbios_processor_info_type_desc(p.smbpi_ptype),
+ fp, " Processor Type: %u", p.smbpi_ptype);
+
+ switch (p.smbpi_ptype) {
+ case SMB_PROCINFO_T_RV32:
+ case SMB_PROCINFO_T_RV64:
+ case SMB_PROCINFO_T_RV128:
+ oprintf(fp, " RISC-V Additional Processor Information:\n");
+ print_processor_info_riscv(shp, id, fp);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+print_pointdev(smbios_hdl_t *shp, id_t id, FILE *fp)
+{
+ smbios_pointdev_t pd;
+
+ if (smbios_info_pointdev(shp, id, &pd) != 0) {
+ smbios_warn(shp, "failed to read pointer device information");
+ return;
+ }
+
+ desc_printf(smbios_pointdev_type_desc(pd.smbpd_type),
+ fp, " Type: %u", pd.smbpd_type);
+ desc_printf(smbios_pointdev_iface_desc(pd.smbpd_iface),
+ fp, " Interface: %u", pd.smbpd_iface);
+ oprintf(fp, " Buttons: %u\n", pd.smbpd_nbuttons);
+}
+
+static void
print_extprocessor(smbios_hdl_t *shp, id_t id, FILE *fp)
{
int i;
@@ -1617,6 +1698,10 @@ print_struct(smbios_hdl_t *shp, const smbios_struct_t *sp, void *fp)
oprintf(fp, "\n");
print_memdevmap(shp, sp->smbstr_id, fp);
break;
+ case SMB_TYPE_POINTDEV:
+ oprintf(fp, "\n");
+ print_pointdev(shp, sp->smbstr_id, fp);
+ break;
case SMB_TYPE_SECURITY:
oprintf(fp, "\n");
print_hwsec(shp, fp);
@@ -1653,6 +1738,10 @@ print_struct(smbios_hdl_t *shp, const smbios_struct_t *sp, void *fp)
oprintf(fp, "\n");
print_obdevs_ext(shp, sp->smbstr_id, fp);
break;
+ case SMB_TYPE_PROCESSOR_INFO:
+ oprintf(fp, "\n");
+ print_processor_info(shp, sp->smbstr_id, fp);
+ break;
case SUN_OEM_EXT_PROCESSOR:
oprintf(fp, "\n");
print_extprocessor(shp, sp->smbstr_id, fp);
diff --git a/usr/src/cmd/sort/common/initialize.c b/usr/src/cmd/sort/common/initialize.c
index 376fac5b04..bb0089ac72 100644
--- a/usr/src/cmd/sort/common/initialize.c
+++ b/usr/src/cmd/sort/common/initialize.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include "initialize.h"
#ifndef TEXT_DOMAIN
@@ -109,6 +107,18 @@ initialize_pre(sort_t *S)
set_signal_jmp();
}
+static int
+strcoll_cmp(void *s1, void *s2, flag_t f __unused)
+{
+ return (strcoll(s1, s2));
+}
+
+static int
+wcscoll_cmp(void *s1, void *s2, flag_t f __unused)
+{
+ return (wcscoll(s1, s2));
+}
+
void
initialize_post(sort_t *S)
{
@@ -124,7 +134,7 @@ initialize_post(sort_t *S)
field_initialize(S);
if (S->m_single_byte_locale) {
- S->m_compare_fn = (cmp_fcn_t)strcoll;
+ S->m_compare_fn = strcoll_cmp;
S->m_coll_convert = field_convert;
F = S->m_fields_head;
@@ -153,7 +163,7 @@ initialize_post(sort_t *S)
F = F->f_next;
}
} else {
- S->m_compare_fn = (cmp_fcn_t)wcscoll;
+ S->m_compare_fn = wcscoll_cmp;
S->m_coll_convert = field_convert_wide;
F = S->m_fields_head;
diff --git a/usr/src/cmd/truss/print.c b/usr/src/cmd/truss/print.c
index b0cf21d346..c679988bd5 100644
--- a/usr/src/cmd/truss/print.c
+++ b/usr/src/cmd/truss/print.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2017, Joyent, Inc. All rights reserved.
+ * Copyright 2020 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -1635,9 +1635,12 @@ prt_psflags(private_t *pri, secflagset_t val)
secflag_clear(&val, PROC_SEC_NOEXECSTACK);
}
- len = strlen(str);
- ptr = str + len;
- (void) snprintf(ptr, sizeof (str) - len, "|%#x", val);
+ if (val != 0) {
+ len = strlen(str);
+ ptr = str + len;
+ (void) snprintf(ptr, sizeof (str) - len, "|%#x", val);
+ }
+
outstring(pri, str + 1);
}
diff --git a/usr/src/cmd/vgrind/Makefile b/usr/src/cmd/vgrind/Makefile
index 1a1eb08217..b873dedafb 100644
--- a/usr/src/cmd/vgrind/Makefile
+++ b/usr/src/cmd/vgrind/Makefile
@@ -28,7 +28,7 @@
# These are the objects associated with the overall vgrind command.
#
VFONTEDPR= vfontedpr
-RETEST= retest
+RETEST= retest
MACROS= tmac.vgrind
LANGDEFS= vgrindefs
KSHPROG= vgrind
@@ -41,8 +41,8 @@ KSHPROG= vgrind
# installed. We omit it here, so that the NSE doesn't spend cycles
# on it when acquiring and reconciling.
#
-PROG= $(KSHPROG)
-LIBPROG= $(VFONTEDPR) $(LANGDEFS)
+PROG= $(KSHPROG)
+LIBPROG= $(VFONTEDPR) $(LANGDEFS)
TMACPROG= $(MACROS)
VFONTEDPROBJS= vfontedpr.o vgrindefs.o regexp.o
@@ -91,8 +91,8 @@ ROOTTMACPROG= $(TMACPROG:%=$(ROOTTMAC)/%)
#
# Conditional assignments pertinent to installation.
#
-$(ROOTLIB)/$(LANGDEFS) := FILEMODE= $(LIBFILEMODE)
-$(ROOTTMACPROG) := FILEMODE= 0644
+$(ROOTLIB)/$(LANGDEFS) := FILEMODE= $(LIBFILEMODE)
+$(ROOTTMACPROG) := FILEMODE= 0644
#
# The standard set of rules doesn't know about installing into
@@ -116,14 +116,14 @@ $(POFILE): $(POFILES) $(POFILE_KSH)
cat $(POFILES) $(POFILE_KSH) > $@
$(VFONTEDPR): $(VFONTEDPROBJS)
- $(CC) -o $@ $(VFONTEDPROBJS) $(LDFLAGS) $(LDLIBS)
+ $(LINK.c) -o $@ $(VFONTEDPROBJS) $(LDLIBS)
$(POST_PROCESS)
$(LANGDEFS): $(LANGDEFS).src
$(CP) $? $@
$(RETEST): $(RETESTOBJS)
- $(CC) -o $@ $(RETESTOBJS) $(LDFLAGS) $(LDLIBS)
+ $(LINK.c) -o $@ $(RETESTOBJS) $(LDLIBS)
$(POST_PROCESS)
#
@@ -131,7 +131,7 @@ $(RETEST): $(RETESTOBJS)
# matching rules see everything they should. (This is a safety net.)
#
# XXX: ROOTTMAC shouldn't appear as a dependent; it's here as a
-# bandaid(TM) until /usr/lib/tmac becomes a symlink to
+# bandaid(TM) until /usr/lib/tmac becomes a symlink to
# /usr/share/lib/tmac.
#
install: all $(ROOTTMAC) $(ROOTPROG) $(ROOTLIBPROG) $(ROOTTMACPROG)
diff --git a/usr/src/common/ccid/atr.c b/usr/src/common/ccid/atr.c
new file mode 100644
index 0000000000..db83c91ceb
--- /dev/null
+++ b/usr/src/common/ccid/atr.c
@@ -0,0 +1,1604 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * ATR parsing routines shared between userland (ccidadm) and the kernel (CCID
+ * driver)
+ */
+
+#include "atr.h"
+#include <sys/debug.h>
+#include <sys/limits.h>
+#include <sys/sysmacros.h>
+
+#ifdef _KERNEL
+#include <sys/inttypes.h>
+#include <sys/sunddi.h>
+#include <sys/kmem.h>
+#else
+#include <inttypes.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#endif
+
+/*
+ * The ATR must have at least 2 bytes and then may have up to 33 bytes. The
+ * first byte is always TS and the second required byte is T0.
+ */
+#define ATR_TS_IDX 0
+#define ATR_T0_IDX 1
+
+/*
+ * There are two valid values for TS. It must either be 0x3F or 0x3B. This is
+ * required per ISO/IEC 7816-3:2006 section 8.1.
+ */
+#define ATR_TS_INVERSE 0x3F
+#define ATR_TS_DIRECT 0x3B
+
+/*
+ * After TS, each word is used to indicate a combination of protocol and the
+ * number of bits defined for that protocol. The lower nibble is treated as the
+ * protocol. The upper nibble is treated to indicate which of four defined words
+ * are present. These are usually referred to as TA, TB, TC, and TD. TD is
+ * always used to indicate the next protocol and the number of bytes present for
+ * that. T0 works in a similar way, except that it defines the number of
+ * historical bytes present in its protocol section and then it refers to a set
+ * of pre-defined global bytes that may be present.
+ */
+#define ATR_TD_PROT(x) ((x) & 0x0f)
+#define ATR_TD_NBITS(x) (((x) & 0xf0) >> 4)
+#define ATR_TA_MASK 0x1
+#define ATR_TB_MASK 0x2
+#define ATR_TC_MASK 0x4
+#define ATR_TD_MASK 0x8
+
+#define ATR_TA1_FTABLE(x) (((x) & 0xf0) >> 4)
+#define ATR_TA1_DITABLE(x) ((x) & 0x0f)
+
+#define ATR_TA2_CANCHANGE(x) (((x) & 0x80) == 0)
+#define ATR_TA2_HONORTA1(x) (((x) & 0x10) == 0)
+#define ATR_TA2_PROTOCOL(x) ((x) & 0x0f)
+
+/*
+ * When the checksum is required in the ATR, each byte must XOR to zero.
+ */
+#define ATR_CKSUM_TARGET 0
+
+/*
+ * Maximum number of historic ATR bytes. This is limited by the fact that it's a
+ * 4-bit nibble.
+ */
+#define ATR_HISTORICAL_MAX 15
+
+/*
+ * The maximum number of TA, TB, TC, and TD levels that can be encountered in a
+ * given structure. In the best case, there are 30 bytes available (TS, T0, and
+ * TCK use the others). Given that each one of these needs 4 bytes to be
+ * represented, the maximum number of layers that can fit is seven.
+ */
+#define ATR_TI_MAX 7
+
+/*
+ * Defined protocol values. See ISO/IEC 7816-3:2006 8.2.3 for this list.
+ * Reserved values are noted but not defined.
+ */
+#define ATR_PROTOCOL_T0 0
+#define ATR_PROTOCOL_T1 1
+
+#define ATR_T1_TB0_CWI(x) ((x) & 0x0f)
+#define ATR_T1_TB0_BWI(x) (((x) & 0xf0) >> 4)
+#define ATR_T1_TC0_CRC(x) (((x) & 0x01) != 0)
+
+/*
+ * T=2 and T=3 are reserved for future full-duplex operation.
+ * T=4 is reserved for enhanced half-duplex character transmission.
+ * T=5-13 are reserved for future use by ISO/IEC JTC 1/SC 17.
+ * T=14 is for protocols not standardized by ISO/IEC JTC 1/SC 17.
+ */
+#define ATR_PROTOCOL_T15 15
+
+#define ATR_T15_TA0_CLOCK(x) (((x) & 0xc0) >> 6)
+#define ATR_T15_TA0_VOLTAGE(x) ((x) & 0x3f)
+
+#define ATR_T15_TB0_SPU_STANDARD(x) (((x & 0x80)) != 0)
+
+/*
+ * Various definitions for the configuration of historical data. This comes from
+ * ISO/IEC 7816-4:2013 Section 12.1.1.
+ */
+
+/*
+ * The first historical byte is used to indicate the encoding of the data. Only
+ * values 0x00, 0x80-0x8f are defined. All others are proprietary. 0x81-0x8f are
+ * reserved for future use.
+ */
+#define ATR_HIST_CAT_MAND_STATUS 0x00
+#define ATR_HIST_CAT_TLV_STATUS 0x80
+#define ATR_HIST_CAT_RFU_MIN 0x81
+#define ATR_HIST_CAT_RFU_MAX 0x8f
+
+/*
+ * From ISO/IEC 7816-3:2006 Section 8.3.
+ *
+ * The default value for Fi is 372 which is table entry 1. The default value for
+ * Di is 1, which is table entry 1.
+ */
+#define ATR_FI_DEFAULT_INDEX 1
+#define ATR_DI_DEFAULT_INDEX 1
+#define ATR_EXTRA_GUARDTIME_DEFAULT 0
+
+/*
+ * From ISO/IEC 7816-3:2006 Section 10.2.
+ */
+#define ATR_T0_WI_DEFAULT 10
+
+/*
+ * From ISO/IEC 7816-3:2006 Section 11.4.3.
+ */
+#define ATR_T1_CWI_DEFAULT 13
+
+/*
+ * From ISO/IEC 7816-3:2006 Section 11.4.3.
+ */
+#define ATR_T1_BWI_DEFAULT 4
+
+/*
+ * From ISO/IEC 7816-3:2006 Section 11.4.2.
+ */
+#define ATR_T1_IFSC_DEFAULT 32
+
+/*
+ * From ISO/IEC 7816-3:2006 Section 11.4.4
+ */
+#define ATR_T1_CHECKSUM_DEFAULT ATR_T1_CHECKSUM_LRC
+
+/*
+ * Definitions for PPS construction. These are derived from ISO/IEC 7816-3:2006
+ * section 9, Protocol and parameters selection.
+ */
+#define PPS_LEN_MIN 3 /* PPSS, PPS0, PCK */
+#define PPS_LEN_MAX PPS_BUFFER_MAX
+#define PPS_PPSS_INDEX 0
+#define PPS_PPSS_VAL 0xff
+#define PPS_PPS0_INDEX 0x01
+#define PPS_PPS0_PROT(x) ((x) & 0x0f)
+#define PPS_PPS0_PPS1 (1 << 4)
+#define PPS_PPS0_PPS2 (1 << 5)
+#define PPS_PPS0_PPS3 (1 << 6)
+#define PPS_PPS1_SETVAL(f, d) ((((f) & 0x0f) << 4) | ((d) & 0x0f))
+
+/*
+ * This enum and subsequent structure is used to represent a single level of
+ * 'T'. This includes the possibility for all three values to be set and records
+ * the protocol.
+ */
+typedef enum atr_ti_flags {
+ ATR_TI_HAVE_TA = 1 << 0,
+ ATR_TI_HAVE_TB = 1 << 1,
+ ATR_TI_HAVE_TC = 1 << 2,
+ ATR_TI_HAVE_TD = 1 << 3
+} atr_ti_flags_t;
+
+typedef struct atr_ti {
+ uint8_t atrti_protocol;
+ uint8_t atrti_ti_val;
+ uint8_t atrti_td_idx;
+ atr_ti_flags_t atrti_flags;
+ uint8_t atrti_ta;
+ uint8_t atrti_tb;
+ uint8_t atrti_tc;
+ uint8_t atrti_td;
+} atr_ti_t;
+
+typedef enum atr_flags {
+ ATR_F_USES_DIRECT = 1 << 0,
+ ATR_F_USES_INVERSE = 1 << 1,
+ ATR_F_HAS_CHECKSUM = 1 << 2,
+ ATR_F_VALID = 1 << 3
+} atr_flags_t;
+
+
+struct atr_data {
+ atr_flags_t atr_flags;
+ uint8_t atr_nti;
+ atr_ti_t atr_ti[ATR_TI_MAX];
+ uint8_t atr_nhistoric;
+ uint8_t atr_historic[ATR_HISTORICAL_MAX];
+ uint8_t atr_cksum;
+ uint8_t atr_raw[ATR_LEN_MAX];
+ uint8_t atr_nraw;
+};
+
+/*
+ * These tables maps the bit values for Fi from 7816-3:2006 section 8.3 Table 7.
+ */
+static uint_t atr_fi_valtable[16] = {
+ 372, /* 0000 */
+ 372, /* 0001 */
+ 558, /* 0010 */
+ 744, /* 0011 */
+ 1116, /* 0100 */
+ 1488, /* 0101 */
+ 1860, /* 0110 */
+ 0, /* 0111 */
+ 0, /* 1000 */
+ 512, /* 1001 */
+ 768, /* 1010 */
+ 1024, /* 1011 */
+ 1536, /* 1100 */
+ 2048, /* 1101 */
+ 0, /* 1110 */
+ 0 /* 1111 */
+};
+
+static const char *atr_fi_table[16] = {
+ "372", /* 0000 */
+ "372", /* 0001 */
+ "558", /* 0010 */
+ "744", /* 0011 */
+ "1116", /* 0100 */
+ "1488", /* 0101 */
+ "1860", /* 0110 */
+ "RFU", /* 0111 */
+ "RFU", /* 1000 */
+ "512", /* 1001 */
+ "768", /* 1010 */
+ "1024", /* 1011 */
+ "1536", /* 1100 */
+ "2048", /* 1101 */
+ "RFU", /* 1110 */
+ "RFU", /* 1111 */
+};
+
+/*
+ * This table maps the bit values for f(max) from 7816-3:2006 section 8.3
+ * Table 7.
+ */
+static const char *atr_fmax_table[16] = {
+ "4", /* 0000 */
+ "5", /* 0001 */
+ "6", /* 0010 */
+ "8", /* 0011 */
+ "12", /* 0100 */
+ "16", /* 0101 */
+ "20", /* 0110 */
+ "-", /* 0111 */
+ "-", /* 1000 */
+ "5", /* 1001 */
+ "7.5", /* 1010 */
+ "10", /* 1011 */
+ "15", /* 1100 */
+ "20", /* 1101 */
+ "-", /* 1110 */
+ "-", /* 1111 */
+};
+
+/*
+ * This table maps the bit values for Di from 7816-3:2006 section 8.3 Table 8.
+ */
+static uint_t atr_di_valtable[16] = {
+ 0, /* 0000 */
+ 1, /* 0001 */
+ 2, /* 0010 */
+ 4, /* 0011 */
+ 8, /* 0100 */
+ 16, /* 0101 */
+ 32, /* 0110 */
+ 64, /* 0111 */
+ 12, /* 1000 */
+ 20, /* 1001 */
+ 0, /* 1010 */
+ 0, /* 1011 */
+ 0, /* 1100 */
+ 0, /* 1101 */
+ 0, /* 1110 */
+ 0 /* 1111 */
+};
+
+static const char *atr_di_table[16] = {
+ "RFU", /* 0000 */
+ "1", /* 0001 */
+ "2", /* 0010 */
+ "4", /* 0011 */
+ "8", /* 0100 */
+ "16", /* 0101 */
+ "32", /* 0110 */
+ "64", /* 0111 */
+ "12", /* 1000 */
+ "20", /* 1001 */
+ "RFU", /* 1010 */
+ "RFU", /* 1011 */
+ "RFU", /* 1100 */
+ "RFU", /* 1101 */
+ "RFU", /* 1110 */
+ "RFU", /* 1111 */
+};
+
+/*
+ * This table maps the bit values for the clock stop indicator from 7816-3:2006
+ * section 8.3 Table 9.
+ */
+static const char *atr_clock_table[4] = {
+ "disallowed", /* 00 */
+ "signal low", /* 01 */
+ "signal high", /* 10 */
+ "signal low or high" /* 11 */
+};
+
+uint_t
+atr_fi_index_to_value(uint8_t val)
+{
+ if (val >= ARRAY_SIZE(atr_fi_valtable)) {
+ return (0);
+ }
+
+ return (atr_fi_valtable[val]);
+}
+
+const char *
+atr_fi_index_to_string(uint8_t val)
+{
+ if (val >= ARRAY_SIZE(atr_fi_table)) {
+ return ("<invalid>");
+ }
+
+ return (atr_fi_table[val]);
+}
+
+const char *
+atr_fmax_index_to_string(uint8_t val)
+{
+ if (val >= ARRAY_SIZE(atr_fmax_table)) {
+ return ("<invalid>");
+ }
+
+ return (atr_fmax_table[val]);
+}
+
+uint_t
+atr_di_index_to_value(uint8_t val)
+{
+ if (val >= ARRAY_SIZE(atr_di_valtable)) {
+ return (0);
+ }
+
+ return (atr_di_valtable[val]);
+}
+const char *
+atr_di_index_to_string(uint8_t val)
+{
+ if (val >= ARRAY_SIZE(atr_di_table)) {
+ return ("<invalid>");
+ }
+
+ return (atr_di_table[val]);
+}
+
+const char *
+atr_clock_stop_to_string(atr_clock_stop_t val)
+{
+ if (val >= ARRAY_SIZE(atr_clock_table)) {
+ return ("<invalid>");
+ }
+
+ return (atr_clock_table[val]);
+}
+
+const char *
+atr_protocol_to_string(atr_protocol_t prot)
+{
+ if (prot == ATR_P_NONE) {
+ return ("none");
+ }
+
+ if ((prot & ATR_P_T0) == ATR_P_T0) {
+ return ("T=0");
+ } else if ((prot & ATR_P_T1) == ATR_P_T1) {
+ return ("T=1");
+ } else {
+ return ("T=0, T=1");
+ }
+}
+
+const char *
+atr_convention_to_string(atr_convention_t conv)
+{
+ if (conv == ATR_CONVENTION_DIRECT) {
+ return ("direct");
+ } else if (conv == ATR_CONVENTION_INVERSE) {
+ return ("inverse");
+ } else {
+ return ("<invalid convention>");
+ }
+}
+
+const char *
+atr_strerror(atr_parsecode_t code)
+{
+ switch (code) {
+ case ATR_CODE_OK:
+ return ("ATR parsed successfully");
+ case ATR_CODE_TOO_SHORT:
+ return ("Specified buffer too short");
+ case ATR_CODE_TOO_LONG:
+ return ("Specified buffer too long");
+ case ATR_CODE_INVALID_TS:
+ return ("ATR has invalid TS byte value");
+ case ATR_CODE_OVERRUN:
+ return ("ATR data requires more bytes than provided");
+ case ATR_CODE_UNDERRUN:
+ return ("ATR data did not use all provided bytes");
+ case ATR_CODE_CHECKSUM_ERROR:
+ return ("ATR data did not checksum correctly");
+ case ATR_CODE_INVALID_TD1:
+ return ("ATR data has invalid protocol in TD1");
+ default:
+ return ("Unknown Parse Code");
+ }
+}
+
+static uint_t
+atr_count_cbits(uint8_t x)
+{
+ uint_t ret = 0;
+
+ if (x & ATR_TA_MASK)
+ ret++;
+ if (x & ATR_TB_MASK)
+ ret++;
+ if (x & ATR_TC_MASK)
+ ret++;
+ if (x & ATR_TD_MASK)
+ ret++;
+ return (ret);
+}
+
+/*
+ * Parse out ATR values. Focus on only parsing it and not interpreting it.
+ * Interpretation should be done in other functions that can walk over the data
+ * and be more protocol-aware.
+ */
+atr_parsecode_t
+atr_parse(const uint8_t *buf, size_t len, atr_data_t *data)
+{
+ uint_t nhist, cbits, ncbits, idx, Ti, prot;
+ uint_t ncksum = 0;
+ atr_ti_t *atp;
+
+ /*
+ * Zero out data in case someone's come back around for another loop on
+ * the same data.
+ */
+ bzero(data, sizeof (atr_data_t));
+
+ if (len < ATR_LEN_MIN) {
+ return (ATR_CODE_TOO_SHORT);
+ }
+
+ if (len > ATR_LEN_MAX) {
+ return (ATR_CODE_TOO_LONG);
+ }
+
+ if (buf[ATR_TS_IDX] != ATR_TS_INVERSE &&
+ buf[ATR_TS_IDX] != ATR_TS_DIRECT) {
+ return (ATR_CODE_INVALID_TS);
+ }
+
+ bcopy(buf, data->atr_raw, len);
+ data->atr_nraw = len;
+
+ if (buf[ATR_TS_IDX] == ATR_TS_DIRECT) {
+ data->atr_flags |= ATR_F_USES_DIRECT;
+ } else {
+ data->atr_flags |= ATR_F_USES_INVERSE;
+ }
+
+ /*
+ * The protocol of T0 is the number of historical bits present.
+ */
+ nhist = ATR_TD_PROT(buf[ATR_T0_IDX]);
+ cbits = ATR_TD_NBITS(buf[ATR_T0_IDX]);
+ idx = ATR_T0_IDX + 1;
+ ncbits = atr_count_cbits(cbits);
+
+ /*
+ * Ti is used to track the current iteration of T[A,B,C,D] that we are
+ * on, as the ISO/IEC standard suggests. The way that values are
+ * interpreted depends on the value of Ti.
+ *
+ * When Ti is one, TA, TB, and TC represent global properties. TD's
+ * protocol represents the preferred protocol.
+ *
+ * When Ti is two, TA, TB, and TC also represent global properties.
+ * However, TC only has meaning if the protocol is T=0.
+ *
+ * When Ti is 15, it indicates more global properties.
+ *
+ * For all other values of Ti, the meaning depends on the protocol in
+ * question and they are all properties specific to that protocol.
+ */
+ Ti = 1;
+ /*
+ * Initialize prot to an invalid protocol to help us deal with the
+ * normal workflow and make sure that we don't mistakenly do anything.
+ */
+ prot = UINT32_MAX;
+ for (;;) {
+ atp = &data->atr_ti[data->atr_nti];
+ data->atr_nti++;
+ ASSERT3U(data->atr_nti, <=, ATR_TI_MAX);
+
+ /*
+ * Make sure that we have enough space to read all the cbits.
+ * idx points to the first cbit, which could also potentially be
+ * over the length of the buffer. This is why we subtract one
+ * from idx when doing the calculation.
+ */
+ if (idx - 1 + ncbits >= len) {
+ return (ATR_CODE_OVERRUN);
+ }
+
+ ASSERT3U(Ti, !=, 0);
+
+ /*
+ * At the moment we opt to ignore reserved protocols.
+ */
+ atp->atrti_protocol = prot;
+ atp->atrti_ti_val = Ti;
+ atp->atrti_td_idx = idx - 1;
+
+ if (cbits & ATR_TA_MASK) {
+ atp->atrti_flags |= ATR_TI_HAVE_TA;
+ atp->atrti_ta = buf[idx];
+ idx++;
+ }
+
+ if (cbits & ATR_TB_MASK) {
+ atp->atrti_flags |= ATR_TI_HAVE_TB;
+ atp->atrti_tb = buf[idx];
+ idx++;
+ }
+
+ if (cbits & ATR_TC_MASK) {
+ atp->atrti_flags |= ATR_TI_HAVE_TC;
+ atp->atrti_tc = buf[idx];
+ idx++;
+ }
+
+ if (cbits & ATR_TD_MASK) {
+ atp->atrti_flags |= ATR_TI_HAVE_TD;
+ atp->atrti_td = buf[idx];
+ cbits = ATR_TD_NBITS(buf[idx]);
+ prot = ATR_TD_PROT(buf[idx]);
+ ncbits = atr_count_cbits(cbits);
+ if (prot != 0)
+ ncksum = 1;
+
+ /*
+ * T=15 is not allowed in TD1 per 8.2.3.
+ */
+ if (Ti == 1 && prot == 0xf)
+ return (ATR_CODE_INVALID_TD1);
+
+ idx++;
+ /*
+ * Encountering TD means that once we take the next loop
+ * and we need to increment Ti.
+ */
+ Ti++;
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * We've parsed all of the cbits. At this point, we should take into
+ * account all of the historical bits and potentially the checksum.
+ */
+ if (idx - 1 + nhist + ncksum >= len) {
+ return (ATR_CODE_OVERRUN);
+ }
+
+ if (idx + nhist + ncksum != len) {
+ return (ATR_CODE_UNDERRUN);
+ }
+
+ if (nhist > 0) {
+ data->atr_nhistoric = nhist;
+ bcopy(&buf[idx], data->atr_historic, nhist);
+ }
+
+ if (ncksum > 0) {
+ size_t i;
+ uint8_t val;
+
+ /*
+ * Per ISO/IEC 7816-3:2006 Section 8.2.5 the checksum is all
+ * bytes excluding TS. Therefore, we must start at byte 1.
+ */
+ for (val = 0, i = 1; i < len; i++) {
+ val ^= buf[i];
+ }
+
+ if (val != ATR_CKSUM_TARGET) {
+ return (ATR_CODE_CHECKSUM_ERROR);
+ }
+ data->atr_flags |= ATR_F_HAS_CHECKSUM;
+ data->atr_cksum = buf[len - 1];
+ }
+
+ data->atr_flags |= ATR_F_VALID;
+ return (ATR_CODE_OK);
+}
+
+uint8_t
+atr_fi_default_index(void)
+{
+ return (ATR_FI_DEFAULT_INDEX);
+}
+
+uint8_t
+atr_di_default_index(void)
+{
+ return (ATR_DI_DEFAULT_INDEX);
+}
+
+/*
+ * Parse the data to determine which protocols are supported in this atr data.
+ * Based on this, users can come and ask us to fill in protocol information.
+ */
+atr_protocol_t
+atr_supported_protocols(atr_data_t *data)
+{
+ uint_t i;
+ atr_protocol_t prot;
+
+ if ((data->atr_flags & ATR_F_VALID) == 0)
+ return (ATR_P_NONE);
+
+ /*
+ * Based on 8.2.3 of ISO/IEC 7816-3:2006, if TD1 is present, then that
+ * indicates the first protocol. However, if it is not present, then
+ * that implies that T=0 is the only supported protocol. Otherwise, all
+ * protocols are referenced in ascending order. The first entry in
+ * atr_ti refers to data from T0, so the protocol in the second entry
+ * would have the TD1 data.
+ */
+ if (data->atr_nti < 2) {
+ return (ATR_P_T0);
+ }
+
+ prot = ATR_P_NONE;
+ for (i = 0; i < data->atr_nti; i++) {
+ switch (data->atr_ti[i].atrti_protocol) {
+ case ATR_PROTOCOL_T0:
+ prot |= ATR_P_T0;
+ break;
+ case ATR_PROTOCOL_T1:
+ prot |= ATR_P_T1;
+ break;
+ default:
+ /*
+ * T=15 is not a protocol, and all other protocol values
+ * are currently reserved for future use.
+ */
+ continue;
+ }
+ }
+
+ /*
+ * It's possible we've found nothing specific in the above loop (for
+ * example, only T=15 global bits were found). In that case, the card
+ * defaults to T=0.
+ */
+ if (prot == ATR_P_NONE)
+ prot = ATR_P_T0;
+
+ return (prot);
+}
+
+boolean_t
+atr_params_negotiable(atr_data_t *data)
+{
+ /* If for some reason we're called with invalid data, assume it's not */
+ if ((data->atr_flags & ATR_F_VALID) == 0)
+ return (B_FALSE);
+
+
+ /*
+ * Whether or not we're negotiable is in the second global page, so atr
+ * index 1. If TA2 is missing, then the card always is negotiable.
+ */
+ if (data->atr_nti < 2 ||
+ (data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TA) == 0) {
+ return (B_TRUE);
+ }
+
+ if (ATR_TA2_CANCHANGE(data->atr_ti[1].atrti_ta)) {
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+atr_protocol_t
+atr_default_protocol(atr_data_t *data)
+{
+ uint8_t prot;
+
+ if ((data->atr_flags & ATR_F_VALID) == 0)
+ return (ATR_P_NONE);
+ /*
+ * If we don't have an TA2 byte, then the system defaults to T=0.
+ */
+ if (data->atr_nti < 2) {
+ return (ATR_P_T0);
+ }
+
+ /*
+ * If TA2 is present, then it encodes the default protocol. Otherwise,
+ * we have to grab the protocol value from TD1, which is called the
+ * 'first offered protocol'.
+ */
+ if ((data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TA) != 0) {
+ prot = ATR_TA2_PROTOCOL(data->atr_ti[1].atrti_ta);
+ } else {
+ prot = data->atr_ti[1].atrti_protocol;
+ }
+
+ switch (prot) {
+ case ATR_PROTOCOL_T0:
+ return (ATR_P_T0);
+ case ATR_PROTOCOL_T1:
+ return (ATR_P_T1);
+ default:
+ return (ATR_P_NONE);
+ }
+}
+
+uint8_t
+atr_fi_index(atr_data_t *data)
+{
+ if (data->atr_nti < 1) {
+ return (ATR_FI_DEFAULT_INDEX);
+ }
+
+ /*
+ * If TA is specified, it is present in TA1. TA2 may override its
+ * presence, so if it is here, check that first to determine whether or
+ * not we should check TA1.
+ */
+ if (data->atr_nti >= 2 &&
+ (data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TA) != 0) {
+ if (!ATR_TA2_HONORTA1(data->atr_ti[1].atrti_ta)) {
+ return (ATR_FI_DEFAULT_INDEX);
+ }
+ }
+
+ if ((data->atr_ti[0].atrti_flags & ATR_TI_HAVE_TA) != 0) {
+ return (ATR_TA1_FTABLE(data->atr_ti[0].atrti_ta));
+ }
+
+ return (ATR_FI_DEFAULT_INDEX);
+}
+
+uint8_t
+atr_di_index(atr_data_t *data)
+{
+ if (data->atr_nti < 1) {
+ return (ATR_DI_DEFAULT_INDEX);
+ }
+
+ /*
+ * If TA is specified, it is present in TA1. TA2 may override its
+ * presence, so if it is here, check that first to determine whether or
+ * not we should check TA1.
+ */
+ if (data->atr_nti >= 2 &&
+ (data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TA) != 0) {
+ if (!ATR_TA2_HONORTA1(data->atr_ti[1].atrti_ta)) {
+ return (ATR_DI_DEFAULT_INDEX);
+ }
+ }
+
+ if ((data->atr_ti[0].atrti_flags & ATR_TI_HAVE_TA) != 0) {
+ return (ATR_TA1_DITABLE(data->atr_ti[0].atrti_ta));
+ }
+
+ return (ATR_DI_DEFAULT_INDEX);
+}
+
+atr_convention_t
+atr_convention(atr_data_t *data)
+{
+ if ((data->atr_flags & ATR_F_USES_DIRECT) != 0) {
+ return (ATR_CONVENTION_DIRECT);
+ }
+ return (ATR_CONVENTION_INVERSE);
+}
+
+uint8_t
+atr_extra_guardtime(atr_data_t *data)
+{
+ if ((data->atr_flags & ATR_F_VALID) == 0)
+ return (ATR_EXTRA_GUARDTIME_DEFAULT);
+
+ if (data->atr_nti >= 1 &&
+ (data->atr_ti[0].atrti_flags & ATR_TI_HAVE_TC) != 0) {
+ return (data->atr_ti[0].atrti_tc);
+ }
+
+ return (ATR_EXTRA_GUARDTIME_DEFAULT);
+}
+
+uint8_t
+atr_t0_wi(atr_data_t *data)
+{
+ if ((data->atr_flags & ATR_F_VALID) == 0)
+ return (ATR_T0_WI_DEFAULT);
+
+ /*
+ * This is stored in the optional global byte in TC2; however, it only
+ * applies to T=0.
+ */
+ if (data->atr_nti >= 2 &&
+ data->atr_ti[1].atrti_protocol == ATR_PROTOCOL_T0 &&
+ (data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TC) != 0) {
+ return (data->atr_ti[1].atrti_tc);
+ }
+
+ return (ATR_T0_WI_DEFAULT);
+}
+
+uint8_t
+atr_t1_cwi(atr_data_t *data)
+{
+ uint8_t i;
+
+ if (data->atr_nti <= 2) {
+ return (ATR_T1_CWI_DEFAULT);
+ }
+
+ for (i = 2; i < data->atr_nti; i++) {
+ if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T1) {
+ if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TB) !=
+ 0) {
+ uint8_t tb = data->atr_ti[i].atrti_tb;
+ return (ATR_T1_TB0_CWI(tb));
+ }
+
+ return (ATR_T1_CWI_DEFAULT);
+ }
+ }
+
+ return (ATR_T1_CWI_DEFAULT);
+}
+
+atr_clock_stop_t
+atr_clock_stop(atr_data_t *data)
+{
+ uint8_t i;
+
+ for (i = 0; i < data->atr_nti; i++) {
+ if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T15) {
+ if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TA) !=
+ 0) {
+ uint8_t ta = data->atr_ti[i].atrti_ta;
+ return (ATR_T15_TA0_CLOCK(ta));
+ }
+
+ return (ATR_CLOCK_STOP_NONE);
+ }
+ }
+
+ return (ATR_CLOCK_STOP_NONE);
+}
+
+atr_t1_checksum_t
+atr_t1_checksum(atr_data_t *data)
+{
+ uint8_t i;
+
+ if (data->atr_nti <= 2) {
+ return (ATR_T1_CHECKSUM_DEFAULT);
+ }
+
+ for (i = 2; i < data->atr_nti; i++) {
+ if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T1) {
+ if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TC) !=
+ 0) {
+ if (ATR_T1_TC0_CRC(data->atr_ti[i].atrti_tc)) {
+ return (ATR_T1_CHECKSUM_CRC);
+ } else {
+ return (ATR_T1_CHECKSUM_LRC);
+ }
+ }
+
+ return (ATR_T1_CHECKSUM_DEFAULT);
+ }
+ }
+
+ return (ATR_T1_CHECKSUM_DEFAULT);
+
+}
+
+uint8_t
+atr_t1_bwi(atr_data_t *data)
+{
+ uint8_t i;
+
+ if (data->atr_nti <= 2) {
+ return (ATR_T1_BWI_DEFAULT);
+ }
+
+ for (i = 2; i < data->atr_nti; i++) {
+ if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T1) {
+ if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TB) !=
+ 0) {
+ uint8_t tb = data->atr_ti[i].atrti_tb;
+ return (ATR_T1_TB0_BWI(tb));
+ }
+
+ return (ATR_T1_BWI_DEFAULT);
+ }
+ }
+
+ return (ATR_T1_BWI_DEFAULT);
+}
+
+uint8_t
+atr_t1_ifsc(atr_data_t *data)
+{
+ uint8_t i;
+
+ if (data->atr_nti <= 2) {
+ return (ATR_T1_IFSC_DEFAULT);
+ }
+
+ for (i = 2; i < data->atr_nti; i++) {
+ if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T1) {
+ if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TA) !=
+ 0) {
+ return (data->atr_ti[i].atrti_ta);
+ }
+
+ return (ATR_T1_IFSC_DEFAULT);
+ }
+ }
+
+ return (ATR_T1_IFSC_DEFAULT);
+}
+
+/*
+ * Attempt to determine which set of data rates we should be able to use for a
+ * given class of protocol. Here we want to do the calculation based on the CCID
+ * specification, section 9.4.x. To use these higher rates we need:
+ *
+ * + Reader's data rate > frequency * Di / Fi.
+ *
+ * To determine which rate and frequency we use, we look at the reader's
+ * features. If the reader supports both the Automatic baud rate and automatic
+ * ICC clock frequency change, then we use the _maximum_ rate. Otherwise we will
+ * indicate that we can use the ATR's properties, but will require changing the
+ * default data rate.
+ *
+ * Now, some ICC devices are not negotiable. In those cases, we'll see if we can
+ * fit it in with either the default or maximum data rates. If not, then we'll
+ * not be able to support this card.
+ *
+ * There are two wrinkles that exist in this. The first is supported frequencies
+ * and data rates. If there are no additional data rates supported, then all of
+ * the data rates between the default and max are supported. If not, then only
+ * those specified in the data rates array are supported.
+ *
+ * The second hurdle is that we need to do this division and try and avoid the
+ * pitfalls of floating point arithmetic, as floating point is not allowed in
+ * the kernel (and this is shared). Importantly that means only integers are
+ * allowed here.
+ */
+atr_data_rate_choice_t
+atr_data_rate(atr_data_t *data, ccid_class_descr_t *class, uint32_t *rates,
+ uint_t nrates, uint32_t *dataratep)
+{
+ uint_t nfeats = CCID_CLASS_F_AUTO_ICC_CLOCK | CCID_CLASS_F_AUTO_BAUD;
+ uint8_t di, fi;
+ uint_t dival, fival;
+ boolean_t autospeed, negotiable, exprates;
+ uint64_t maxval, defval;
+
+ if ((data->atr_flags & ATR_F_VALID) == 0)
+ return (ATR_RATE_UNSUPPORTED);
+
+ di = atr_di_index(data);
+ fi = atr_fi_index(data);
+ dival = atr_di_index_to_value(di);
+ fival = atr_fi_index_to_value(fi);
+ autospeed = (class->ccd_dwFeatures & nfeats) == nfeats;
+ exprates = class->ccd_bNumDataRatesSupported != 0;
+ negotiable = atr_params_negotiable(data);
+
+ /*
+ * We don't support cards with fixed rates at this time as it's not
+ * clear what that rate should be. If it's negotiable, we'll let them
+ * run at the default. Otherwise, we have to fail the request until
+ * we implement the logic to search their data rates.
+ */
+ if (exprates) {
+ if (negotiable) {
+ return (ATR_RATE_USEDEFAULT);
+ }
+ return (ATR_RATE_UNSUPPORTED);
+ }
+
+ /*
+ * This indicates that the card gave us values that were reserved for
+ * future use. If we could negotiate it, then just stick with the
+ * default paramters. Otherwise, return that we can't support this ICC.
+ */
+ if (dival == 0 || fival == 0) {
+ if (negotiable)
+ return (ATR_RATE_USEDEFAULT);
+ return (ATR_RATE_UNSUPPORTED);
+ }
+
+ /*
+ * Calculate the maximum and default values.
+ */
+ maxval = class->ccd_dwMaximumClock * 1000;
+ maxval *= dival;
+ maxval /= fival;
+
+ defval = class->ccd_dwDefaultClock * 1000;
+ defval *= dival;
+ defval /= fival;
+
+ /*
+ * We're allowed any set of data rates between the default and the
+ * maximum. Check if the maximum data rate will work for either the
+ * default or maximum clock. If so, then we can use the cards rates.
+ *
+ * To account for the fact that we may have had a fractional value,
+ * we require a strict greater than comparison.
+ */
+ if ((uint64_t)class->ccd_dwMaxDataRate > maxval ||
+ (uint64_t)class->ccd_dwMaxDataRate > defval) {
+ if (autospeed) {
+ return (ATR_RATE_USEATR);
+ }
+ }
+
+ /*
+ * If the CCID reader can't handle the ICC's proposed rates, then fall
+ * back to the defaults if we're allowed to negotiate. Otherwise, we're
+ * not able to use this ICC.
+ */
+ if (negotiable) {
+ return (ATR_RATE_USEDEFAULT);
+ }
+
+ return (ATR_RATE_UNSUPPORTED);
+}
+
+void
+atr_data_reset(atr_data_t *data)
+{
+ bzero(data, sizeof (*data));
+}
+
+#ifdef _KERNEL
+atr_data_t *
+atr_data_alloc(void)
+{
+ return (kmem_zalloc(sizeof (atr_data_t), KM_SLEEP));
+}
+
+void
+atr_data_free(atr_data_t *data)
+{
+ kmem_free(data, sizeof (atr_data_t));
+}
+
+/*
+ * Make sure that the response we got from the ICC is valid. It must pass
+ * checksum and have the PPSS value set correctly. The protocol must match
+ * what we requested; however, the PPS1-3 bits are a bit different. They may
+ * only be set in the response if we set them in the request. However, they
+ * do not have to be set in the response.
+ */
+boolean_t
+atr_pps_valid(void *reqbuf, size_t reqlen, void *respbuf, size_t resplen)
+{
+ uint8_t val, i, reqidx, respidx;
+ uint8_t *req = reqbuf, *resp = respbuf;
+
+ if (resplen > PPS_LEN_MAX || resplen < PPS_LEN_MIN)
+ return (B_FALSE);
+
+ /*
+ * Before we validate the data, make sure the checksum is valid.
+ */
+ for (i = 0, val = 0; i < resplen; i++) {
+ val ^= resp[i];
+ }
+
+ /* Checksum failure */
+ if (val != 0) {
+ return (B_FALSE);
+ }
+
+ /*
+ * We should always have PPSS echoed back as we set it.
+ */
+ if (resp[PPS_PPSS_INDEX] != PPS_PPSS_VAL) {
+ return (B_FALSE);
+ }
+
+ /*
+ * Go through and make sure the number of bytes present makes sense for
+ * the number of bits set in PPS1.
+ */
+ val = PPS_LEN_MIN;
+ if (resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS1)
+ val++;
+ if (resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS2)
+ val++;
+ if (resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS3)
+ val++;
+ if (val != resplen)
+ return (B_FALSE);
+
+ /*
+ * Now we've finally verified that the response is syntactically valid.
+ * We must go through and make sure that it is semantically valid.
+ */
+ if (PPS_PPS0_PROT(req[PPS_PPS0_INDEX]) !=
+ PPS_PPS0_PROT(resp[PPS_PPS0_INDEX])) {
+ return (B_FALSE);
+ }
+
+ /*
+ * When checking the PPS bit and extensions, we first check in the
+ * response as a bit in the request is allowed to not be in the
+ * response. But not the opposite way around. We also have to keep track
+ * of the fact that the index for values will vary.
+ */
+ reqidx = respidx = PPS_PPS0_INDEX + 1;
+ if ((resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS1) != 0) {
+ if ((req[PPS_PPS0_INDEX] & PPS_PPS0_PPS1) == 0) {
+ return (B_FALSE);
+ }
+
+ if (req[reqidx] != resp[respidx]) {
+ return (B_FALSE);
+ }
+
+ reqidx++;
+ respidx++;
+ } else if ((req[PPS_PPS0_INDEX] & PPS_PPS0_PPS1) != 0) {
+ reqidx++;
+ }
+
+ if ((resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS2) != 0) {
+ if ((req[PPS_PPS0_INDEX] & PPS_PPS0_PPS2) == 0) {
+ return (B_FALSE);
+ }
+
+ if (req[reqidx] != resp[respidx]) {
+ return (B_FALSE);
+ }
+
+ reqidx++;
+ respidx++;
+ } else if ((req[PPS_PPS0_INDEX] & PPS_PPS0_PPS2) != 0) {
+ reqidx++;
+ }
+
+ if ((resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS3) != 0) {
+ /*
+ * At this time, we never specify PPS3 in a request. Therefore
+ * if it is present in the response, treat this as an invalid
+ * request.
+ */
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+uint_t
+atr_pps_generate(uint8_t *buf, size_t buflen, atr_protocol_t prot,
+ boolean_t pps1, uint8_t fi, uint8_t di, boolean_t pps2, uint8_t spu)
+{
+ uint8_t protval, cksum, i;
+ uint_t len = 0;
+
+ if (buflen < PPS_BUFFER_MAX)
+ return (0);
+
+ buf[PPS_PPSS_INDEX] = PPS_PPSS_VAL;
+ switch (prot) {
+ case ATR_P_T0:
+ protval = 0;
+ break;
+ case ATR_P_T1:
+ protval = 1;
+ break;
+ default:
+ return (0);
+ }
+
+ buf[PPS_PPS0_INDEX] = PPS_PPS0_PROT(protval);
+ len = 2;
+ if (pps1) {
+ buf[PPS_PPS0_INDEX] |= PPS_PPS0_PPS1;
+ buf[len++] = PPS_PPS1_SETVAL(fi, di);
+ }
+
+ if (pps2) {
+ buf[PPS_PPS0_INDEX] |= PPS_PPS0_PPS2;
+ buf[len++] = spu;
+ }
+
+ /*
+ * The checksum must xor to zero.
+ */
+ for (i = 0, cksum = 0; i < len; i++) {
+ cksum ^= buf[i];
+ }
+ buf[len++] = cksum;
+ return (len);
+}
+
+/*
+ * The caller of this wants to know if the Fi/Di values that they proposed were
+ * accepted. The caller must have already called atr_pps_valid(). At this point,
+ * we can say that the value was accepted if the PPS1 bit is set.
+ */
+boolean_t
+atr_pps_fidi_accepted(void *respbuf, size_t len)
+{
+ uint8_t *resp = respbuf;
+ return ((resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS1) != 0);
+}
+
+#else /* !_KERNEL */
+atr_data_t *
+atr_data_alloc(void)
+{
+ return (calloc(1, sizeof (atr_data_t)));
+}
+
+void
+atr_data_free(atr_data_t *data)
+{
+ if (data == NULL)
+ return;
+ free(data);
+}
+
+/*
+ * This table maps the bit values for Fi from 7816-3:2006 section 8.3 Table 9.
+ * The table is up to 6 bits wide. Entries not present are RFU. We use NULL as a
+ * sentinel to indicate that.
+ */
+static const char *atr_voltage_table[64] = {
+ NULL, /* 00 0000 */
+ "5V", /* 00 0001 */
+ "3V", /* 00 0010 */
+ "5V, 3V", /* 00 0011 */
+ "1.5V", /* 00 0100 */
+ NULL, /* 00 0101 */
+ "3V, 1.5V", /* 00 0110 */
+ "5V, 3V, 1.5V" /* 00 0111 */
+};
+
+static void
+atr_data_dump_ta(atr_ti_t *atp, FILE *out, uint_t level)
+{
+ uint8_t ta;
+
+ if (!(atp->atrti_flags & ATR_TI_HAVE_TA)) {
+ return;
+ }
+
+ ta = atp->atrti_ta;
+ (void) fprintf(out, " %c%c%c+-> TA%u 0x%02x",
+ atp->atrti_flags & ATR_TI_HAVE_TD ? '|' : ' ',
+ atp->atrti_flags & ATR_TI_HAVE_TC ? '|' : ' ',
+ atp->atrti_flags & ATR_TI_HAVE_TB ? '|' : ' ',
+ atp->atrti_ti_val, ta);
+ switch (atp->atrti_ti_val) {
+ case 1:
+ (void) fprintf(out, "; Fi: %s, F(max): %s MHz, Di: %s",
+ atr_fi_table[ATR_TA1_FTABLE(ta)],
+ atr_fmax_table[ATR_TA1_FTABLE(ta)],
+ atr_di_table[ATR_TA1_DITABLE(ta)]);
+ break;
+ case 2:
+ (void) fprintf(out, "; ICC in %s mode; %shonoring TA1; default "
+ "T=%u",
+ ATR_TA2_CANCHANGE(ta) ? "negotiable" : "specific",
+ ATR_TA2_HONORTA1(ta) ? "" : "not ",
+ ATR_TA2_PROTOCOL(ta));
+ break;
+ default:
+ switch (atp->atrti_protocol) {
+ case ATR_PROTOCOL_T1:
+ if (level != 0)
+ break;
+ if (ta == 0 || ta == 0xff) {
+ (void) fprintf(out, "; IFSC: RFU");
+ } else {
+ (void) fprintf(out, "; IFSC: %u", ta);
+ }
+ break;
+ case ATR_PROTOCOL_T15:
+ if (level != 0)
+ break;
+ (void) fprintf(out, "; Clock stop: %s, Supported "
+ "Voltage: %s",
+ atr_clock_table[ATR_T15_TA0_CLOCK(ta)],
+ atr_voltage_table[ATR_T15_TA0_VOLTAGE(ta)] != NULL ?
+ atr_voltage_table[ATR_T15_TA0_VOLTAGE(ta)] : "RFU");
+ break;
+ default:
+ break;
+ }
+ }
+ (void) fprintf(out, "\n");
+}
+
+static void
+atr_data_dump_tb(atr_ti_t *atp, FILE *out, uint_t level)
+{
+ uint8_t tb;
+
+ if (!(atp->atrti_flags & ATR_TI_HAVE_TB)) {
+ return;
+ }
+
+ tb = atp->atrti_tb;
+ (void) fprintf(out, " %c%c+--> TB%u 0x%02x",
+ atp->atrti_flags & ATR_TI_HAVE_TD ? '|' : ' ',
+ atp->atrti_flags & ATR_TI_HAVE_TC ? '|' : ' ',
+ atp->atrti_ti_val, tb);
+ switch (atp->atrti_ti_val) {
+ case 1:
+ case 2:
+ (void) fprintf(out, "; deprecated");
+ break;
+ default:
+ switch (atp->atrti_protocol) {
+ case ATR_PROTOCOL_T1:
+ if (level != 0)
+ break;
+ (void) fprintf(out, "; CWI: %u, BWI: %u\n",
+ ATR_T1_TB0_CWI(tb),
+ ATR_T1_TB0_BWI(tb));
+ break;
+ case ATR_PROTOCOL_T15:
+ if (level != 0)
+ break;
+ (void) fprintf(out, "; SPU: %s", tb == 0 ? "not used" :
+ ATR_T15_TB0_SPU_STANDARD(tb) ? "standard" :
+ "proprietary");
+ break;
+ default:
+ break;
+ }
+ }
+ (void) fprintf(out, "\n");
+}
+
+static void
+atr_data_dump_tc(atr_ti_t *atp, FILE *out, uint_t level)
+{
+ uint8_t tc;
+
+ if (!(atp->atrti_flags & ATR_TI_HAVE_TC)) {
+ return;
+ }
+
+ tc = atp->atrti_tc;
+ (void) fprintf(out, " %c+---> TC%u 0x%02x",
+ atp->atrti_flags & ATR_TI_HAVE_TD ? '|' : ' ',
+ atp->atrti_ti_val, tc);
+
+ switch (atp->atrti_ti_val) {
+ case 1:
+ (void) fprintf(out, "; Extra Guard Time Integer: %u", tc);
+ break;
+ case 2:
+ if (atp->atrti_protocol != ATR_PROTOCOL_T0) {
+ (void) fprintf(out, "; illegal value -- only valid for "
+ "T=0");
+ } else {
+ (void) fprintf(out, "; Waiting Time Integer: %u", tc);
+ }
+ break;
+ default:
+ switch (atp->atrti_protocol) {
+ case ATR_PROTOCOL_T1:
+ if (level != 0)
+ break;
+ (void) fprintf(out, "; Error Detection Code: %s",
+ ATR_T1_TC0_CRC(tc) ? "CRC" : "LRC");
+ break;
+ default:
+ break;
+ }
+ }
+ (void) fprintf(out, "\n");
+}
+
+void
+atr_data_hexdump(const uint8_t *buf, size_t nbytes, FILE *out)
+{
+ size_t i, j;
+
+ /* Print out the header */
+ (void) fprintf(out, "%*s 0", 4, "");
+ for (i = 1; i < 16; i++) {
+ if (i % 4 == 0 && i % 16 != 0) {
+ (void) fprintf(out, " ");
+ }
+
+ (void) fprintf(out, "%2x", i);
+ }
+ (void) fprintf(out, " 0123456789abcdef\n");
+
+ /* Print out data */
+ for (i = 0; i < nbytes; i++) {
+
+ if (i % 16 == 0) {
+ (void) fprintf(out, "%04x: ", i);
+ }
+
+ if (i % 4 == 0 && i % 16 != 0) {
+ (void) fprintf(out, " ");
+ }
+
+ (void) fprintf(out, "%02x", buf[i]);
+
+ if (i % 16 == 15 || i + 1 == nbytes) {
+ for (j = (i % 16) + 1; j < 16; j++) {
+ if (j % 4 == 0 && j % 16 != 0) {
+ (void) fprintf(out, " ");
+ }
+
+ (void) fprintf(out, " ");
+ }
+
+ (void) fprintf(out, " ");
+ for (j = i - (i % 16); j <= i; j++) {
+ (void) fprintf(out, "%c",
+ isprint(buf[j]) ? buf[j] : '.');
+ }
+ (void) printf("\n");
+ }
+ }
+}
+
+static void
+atr_data_hexdump_historical(atr_data_t *data, FILE *out)
+{
+ (void) fprintf(out, "Dumping raw historical bytes\n");
+
+ atr_data_hexdump(data->atr_historic, data->atr_nhistoric, out);
+}
+
+static void
+atr_data_dump_historical(atr_data_t *data, FILE *out)
+{
+ uint8_t cat;
+
+ (void) fprintf(out, "Historic Data: %u bytes", data->atr_nhistoric);
+ if (data->atr_nhistoric == 0) {
+ (void) fprintf(out, "\n");
+ return;
+ }
+
+ cat = data->atr_historic[0];
+ (void) fprintf(out, "; format (0x%02x) ", cat);
+ if (cat == ATR_HIST_CAT_MAND_STATUS) {
+ (void) fprintf(out, "card status, not shown");
+ } else if (cat == ATR_HIST_CAT_TLV_STATUS) {
+ (void) fprintf(out, "COMPACT-TLV, not shown");
+ } else if (cat >= ATR_HIST_CAT_RFU_MIN && cat <= ATR_HIST_CAT_RFU_MAX) {
+ (void) fprintf(out, "reserved\n");
+ atr_data_hexdump_historical(data, out);
+ return;
+ } else {
+ (void) fprintf(out, "proprietary\n");
+ atr_data_hexdump_historical(data, out);
+ return;
+ }
+}
+
+void
+atr_data_dump(atr_data_t *data, FILE *out)
+{
+ uint8_t i, level;
+ if ((data->atr_flags & ATR_F_VALID) == 0)
+ return;
+
+ (void) fprintf(out, "TS 0x%02u - ", data->atr_raw[0]);
+ if (data->atr_flags & ATR_F_USES_DIRECT) {
+ (void) fprintf(out, "direct convention\n");
+ } else {
+ (void) fprintf(out, "inverse convention\n");
+ }
+
+ level = 0;
+ for (i = 0; i < data->atr_nti; i++) {
+ atr_ti_t *atp = &data->atr_ti[i];
+
+ /*
+ * Various protocols may appear multiple times, indicating
+ * different sets of bits each time. When dealing with T0 and
+ * TD1, the protocol doesn't matter. Otherwise if we have the
+ * same value, we should increment this.
+ */
+ if (i <= 2) {
+ level = 0;
+ } else if (atp->atrti_protocol ==
+ data->atr_ti[i - 1].atrti_protocol) {
+ level++;
+ } else {
+ level = 0;
+ }
+
+ if (i == 0) {
+ (void) fprintf(out, "T0 ");
+ } else {
+ (void) fprintf(out, "TD%u ", i);
+ }
+ (void) fprintf(out, "0x%02x\n",
+ data->atr_raw[atp->atrti_td_idx]);
+ (void) fprintf(out, " |+-> ");
+ if (i == 0) {
+ (void) fprintf(out, "%u historical bytes\n",
+ data->atr_nhistoric);
+ } else {
+ (void) fprintf(out, "protocol T=%u\n",
+ atp->atrti_protocol);
+ }
+ (void) fprintf(out, " v\n");
+ (void) fprintf(out, " 0r%u%u%u%u\n",
+ atp->atrti_flags & ATR_TI_HAVE_TD ? 1 : 0,
+ atp->atrti_flags & ATR_TI_HAVE_TC ? 1 : 0,
+ atp->atrti_flags & ATR_TI_HAVE_TB ? 1 : 0,
+ atp->atrti_flags & ATR_TI_HAVE_TA ? 1 : 0);
+
+ atr_data_dump_ta(atp, out, level);
+ atr_data_dump_tb(atp, out, level);
+ atr_data_dump_tc(atp, out, level);
+ if (atp->atrti_flags & ATR_TI_HAVE_TD) {
+ (void) fprintf(out, " v\n");
+ }
+ }
+
+ atr_data_dump_historical(data, out);
+
+ if (data->atr_flags & ATR_F_HAS_CHECKSUM) {
+ (void) fprintf(out, "TCK 0x%02x\n", data->atr_cksum);
+ } else {
+ (void) fprintf(out, "TCK ----; Checksum not present\n");
+ }
+
+}
+#endif /* _KERNEL */
diff --git a/usr/src/common/ccid/atr.h b/usr/src/common/ccid/atr.h
new file mode 100644
index 0000000000..50c01a44cb
--- /dev/null
+++ b/usr/src/common/ccid/atr.h
@@ -0,0 +1,198 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+#ifndef _ATR_H
+#define _ATR_H
+
+/*
+ * Parse Answer-To-Reset values. This header file is private to illumos and
+ * should not be shipped or used by applications.
+ *
+ * This is based on ISO/IEC 7816-3:2006. It has been designed such that if newer
+ * revisions come out that define reserved values, they will be ignored until
+ * this code is updated.
+ */
+
+#include <sys/types.h>
+#include <sys/usb/clients/ccid/ccid.h>
+#ifndef _KERNEL
+#include <stdio.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The ATR must have at least 2 bytes and then may have up to 33 bytes.
+ */
+#define ATR_LEN_MIN 2
+#define ATR_LEN_MAX 33
+
+typedef enum atr_parsecode {
+ ATR_CODE_OK = 0,
+ ATR_CODE_TOO_SHORT,
+ ATR_CODE_TOO_LONG,
+ ATR_CODE_INVALID_TS,
+ ATR_CODE_OVERRUN,
+ ATR_CODE_UNDERRUN,
+ ATR_CODE_CHECKSUM_ERROR,
+ ATR_CODE_INVALID_TD1
+} atr_parsecode_t;
+
+typedef enum atr_protocol {
+ ATR_P_NONE = 0,
+ ATR_P_T0 = 1 << 0,
+ ATR_P_T1 = 1 << 1
+} atr_protocol_t;
+
+typedef enum atr_convention {
+ ATR_CONVENTION_DIRECT = 0x00,
+ ATR_CONVENTION_INVERSE = 0x01
+} atr_convention_t;
+
+typedef enum atr_clock_stop {
+ ATR_CLOCK_STOP_NONE = 0x00,
+ ATR_CLOCK_STOP_LOW = 0x01,
+ ATR_CLOCK_STOP_HI = 0x02,
+ ATR_CLOCK_STOP_BOTH = 0x03
+} atr_clock_stop_t;
+
+typedef enum atr_data_rate_choice {
+ /*
+ * Indicates that the reader cannot support the data rate needed for the
+ * ICC.
+ */
+ ATR_RATE_UNSUPPORTED = 0x00,
+ /*
+ * Indicates that the reader supports the ICC present, but must run at
+ * the protocol's default rate (Di index = Fi index = 1)
+ */
+ ATR_RATE_USEDEFAULT = 0x01,
+ /*
+ * The reader supports the Di/Fi values that the ICC proposed in its ATR
+ * and no action beyond setting the parameters of the reader is required
+ * (this may be automatic depending on the reader's dwFeatures).
+ */
+ ATR_RATE_USEATR = 0x02,
+ /*
+ * The reader can use the features of the ATR specified. However, it
+ * must change the data rate or frequency that the card is running at to
+ * proceed.
+ */
+ ATR_RATE_USEATR_SETRATE = 0x03
+} atr_data_rate_choice_t;
+
+typedef enum atr_t1_checksum {
+ ATR_T1_CHECKSUM_LRC = 0x00,
+ ATR_T1_CHECKSUM_CRC = 0x01
+} atr_t1_checksum_t;
+
+typedef struct atr_data atr_data_t;
+
+/*
+ * Allocate and free ATR data.
+ */
+extern atr_data_t *atr_data_alloc(void);
+extern void atr_data_free(atr_data_t *);
+
+/*
+ * Reset an allocated ATR data to be ready to parse something else.
+ */
+extern void atr_data_reset(atr_data_t *);
+
+/*
+ * Parse the ATR data into an opaque structure that organizes the data and
+ * allows for various queries to be made on it later.
+ */
+extern atr_parsecode_t atr_parse(const uint8_t *, size_t, atr_data_t *data);
+extern const char *atr_strerror(atr_parsecode_t);
+
+/*
+ * Get an eumeration of supported protocols in this ATR data. Note that if a
+ * reserved protocol is encountered, we may not report it as we don't know of it
+ * at this time.
+ */
+extern atr_protocol_t atr_supported_protocols(atr_data_t *);
+
+/*
+ * Based on the ATR determine what the default protocol is and whether or not it
+ * supports negotiation. When a ICC is not negotiable, it will always start up
+ * with a specific protocol and parameters based on the ATR and be ready to use.
+ * Otherwise, the card will be in a negotiable mode and be set to a default set
+ * of parameters.
+ */
+extern boolean_t atr_params_negotiable(atr_data_t *);
+extern atr_protocol_t atr_default_protocol(atr_data_t *);
+
+/*
+ * Protocol default values.
+ */
+extern uint8_t atr_fi_default_index(void);
+extern uint8_t atr_di_default_index(void);
+
+/*
+ * Obtain the table indexes that should be used by the device.
+ */
+extern uint8_t atr_fi_index(atr_data_t *);
+extern uint8_t atr_di_index(atr_data_t *);
+extern atr_convention_t atr_convention(atr_data_t *);
+extern uint8_t atr_extra_guardtime(atr_data_t *);
+extern uint8_t atr_t0_wi(atr_data_t *);
+extern atr_t1_checksum_t atr_t1_checksum(atr_data_t *);
+extern uint8_t atr_t1_bwi(atr_data_t *);
+extern uint8_t atr_t1_cwi(atr_data_t *);
+extern atr_clock_stop_t atr_clock_stop(atr_data_t *);
+extern uint8_t atr_t1_ifsc(atr_data_t *);
+
+/*
+ * Use this function to determine what set of Di and Fi values should be used by
+ * a reader, based on the parameters from the ATR and the reader's cclass.
+ */
+extern atr_data_rate_choice_t atr_data_rate(atr_data_t *, ccid_class_descr_t *,
+ uint32_t *, uint_t, uint32_t *);
+
+#ifndef _KERNEL
+extern void atr_data_hexdump(const uint8_t *, size_t, FILE *);
+extern void atr_data_dump(atr_data_t *, FILE *);
+#endif
+
+/*
+ * String and table index values.
+ */
+extern const char *atr_protocol_to_string(atr_protocol_t);
+extern uint_t atr_fi_index_to_value(uint8_t);
+extern const char *atr_fi_index_to_string(uint8_t);
+extern const char *atr_fmax_index_to_string(uint8_t);
+extern uint_t atr_di_index_to_value(uint8_t);
+extern const char *atr_di_index_to_string(uint8_t);
+extern const char *atr_clock_stop_to_string(atr_clock_stop_t);
+extern const char *atr_convention_to_string(atr_convention_t);
+
+/*
+ * Functions for generating and testing PPS values. Before calling
+ * atr_pps_fidi_accepted(), one must call atr_pps_valid().
+ */
+#define PPS_BUFFER_MAX 6
+extern uint_t atr_pps_generate(uint8_t *, size_t, atr_protocol_t, boolean_t,
+ uint8_t, uint8_t, boolean_t, uint8_t);
+extern boolean_t atr_pps_valid(void *, size_t, void *, size_t);
+extern boolean_t atr_pps_fidi_accepted(void *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ATR_H */
diff --git a/usr/src/common/smbios/mktables.sh b/usr/src/common/smbios/mktables.sh
index cd711f6474..bb6c077791 100644
--- a/usr/src/common/smbios/mktables.sh
+++ b/usr/src/common/smbios/mktables.sh
@@ -52,6 +52,8 @@ SMB_POWERSUP_F_ smbios_powersup_flag_name uint_t
SMB_MOMC_ smbios_memdevice_op_capab_name uint_t
SMB_MDF_ smbios_memdevice_flag_name uint_t
SMB_PRC_ smbios_processor_core_flag_name uint_t
+SMB_RV_ISA_ smbios_riscv_isa_name uint64_t
+SMB_RV_PRIV_ smbios_riscv_priv_name uint_t
SMB_TYPE_ smbios_type_name uint_t
SMB_SLCH1_ smbios_slot_ch1_name uint_t
SMB_SLCH2_ smbios_slot_ch2_name uint_t
@@ -97,13 +99,19 @@ SMB_MDR_ smbios_memdevice_rank_desc uint_t
SMB_MTECH_ smbios_memdevice_memtech_desc uint_t
SMB_MOMC_ smbios_memdevice_op_capab_desc uint_t
SMB_OBT_ smbios_onboard_type_desc uint_t
+SMB_PDI_ smbios_pointdev_iface_desc uint_t
+SMB_PDT_ smbios_pointdev_type_desc uint_t
SMB_POC_ smbios_port_conn_desc uint_t
SMB_POT_ smbios_port_type_desc uint_t
SMB_PRC_ smbios_processor_core_flag_desc uint_t
SMB_PRF_ smbios_processor_family_desc uint_t
+SMB_PROCINFO_T smbios_processor_info_type_desc uint_t
SMB_PRS_ smbios_processor_status_desc uint_t
SMB_PRT_ smbios_processor_type_desc uint_t
SMB_PRU_ smbios_processor_upgrade_desc uint_t
+SMB_RV_ISA_ smbios_riscv_isa_desc uint64_t
+SMB_RV_PRIV_ smbios_riscv_priv_desc uint_t
+SMB_RV_WIDTH_ smbios_riscv_width_desc uint_t
SMB_SLCH1_ smbios_slot_ch1_desc uint_t
SMB_SLCH2_ smbios_slot_ch2_desc uint_t
SMB_SLL_ smbios_slot_length_desc uint_t
diff --git a/usr/src/common/smbios/smb_info.c b/usr/src/common/smbios/smb_info.c
index b4f819852b..c4376fd081 100644
--- a/usr/src/common/smbios/smb_info.c
+++ b/usr/src/common/smbios/smb_info.c
@@ -558,13 +558,21 @@ smbios_info_processor(smbios_hdl_t *shp, id_t id, smbios_processor_t *pp)
}
if (smb_libgteq(shp, SMB_VERSION_26)) {
- pp->smbp_family2 = p.smbpr_family2;
+ if (pp->smbp_family == 0xfe) {
+ pp->smbp_family = p.smbpr_family2;
+ }
}
if (smb_libgteq(shp, SMB_VERSION_30)) {
- pp->smbp_corecount2 = p.smbpr_corecount2;
- pp->smbp_coresenabled2 = p.smbpr_coresenabled2;
- pp->smbp_threadcount2 = p.smbpr_threadcount2;
+ if (pp->smbp_corecount == 0xff) {
+ pp->smbp_corecount = p.smbpr_corecount2;
+ }
+ if (pp->smbp_coresenabled == 0xff) {
+ pp->smbp_coresenabled = p.smbpr_coresenabled2;
+ }
+ if (pp->smbp_threadcount == 0xff) {
+ pp->smbp_threadcount = p.smbpr_threadcount2;
+ }
}
return (0);
@@ -1014,6 +1022,20 @@ smbios_info_memdevice(smbios_hdl_t *shp, id_t id, smbios_memdevice_t *mdp)
mdp->smbmd_logical_size = m.smbmdev_logicalsize;
}
+ if (smb_libgteq(shp, SMB_VERSION_33)) {
+ if (m.smbmdev_speed == 0xffff) {
+ mdp->smbmd_extspeed = m.smbmdev_extspeed;
+ } else {
+ mdp->smbmd_extspeed = m.smbmdev_speed;
+ }
+
+ if (m.smbmdev_clkspeed == 0xffff) {
+ mdp->smbmd_extclkspeed = m.smbmdev_extclkspeed;
+ } else {
+ mdp->smbmd_extclkspeed = m.smbmdev_clkspeed;
+ }
+ }
+
return (0);
}
@@ -1563,3 +1585,116 @@ smbios_info_iprobe(smbios_hdl_t *shp, id_t id, smbios_iprobe_t *iprobe)
return (0);
}
+
+int
+smbios_info_processor_info(smbios_hdl_t *shp, id_t id,
+ smbios_processor_info_t *proc)
+{
+ const smb_struct_t *stp = smb_lookup_id(shp, id);
+ smb_processor_info_t pi;
+
+ if (stp == NULL)
+ return (-1); /* errno is set for us */
+
+ if (stp->smbst_hdr->smbh_type != SMB_TYPE_PROCESSOR_INFO)
+ return (smb_set_errno(shp, ESMB_TYPE));
+
+ if (stp->smbst_hdr->smbh_len < sizeof (pi))
+ return (smb_set_errno(shp, ESMB_SHORT));
+
+ bzero(proc, sizeof (*proc));
+ smb_info_bcopy(stp->smbst_hdr, &pi, sizeof (pi));
+
+ if (sizeof (pi) + pi.smbpai_len > stp->smbst_hdr->smbh_len)
+ return (smb_set_errno(shp, ESMB_CORRUPT));
+
+ proc->smbpi_processor = pi.smbpai_proc;
+ proc->smbpi_ptype = pi.smbpai_type;
+
+ return (0);
+}
+
+int
+smbios_info_processor_riscv(smbios_hdl_t *shp, id_t id,
+ smbios_processor_info_riscv_t *riscv)
+{
+ const smb_struct_t *stp = smb_lookup_id(shp, id);
+ const smb_processor_info_t *proc;
+ const smb_processor_info_riscv_t *rv;
+
+ if (stp->smbst_hdr->smbh_type != SMB_TYPE_PROCESSOR_INFO) {
+ return (smb_set_errno(shp, ESMB_TYPE));
+ }
+
+ if (stp->smbst_hdr->smbh_len < sizeof (*proc)) {
+ return (smb_set_errno(shp, ESMB_SHORT));
+ }
+
+ proc = (const smb_processor_info_t *)stp->smbst_hdr;
+ if (sizeof (*proc) + proc->smbpai_len > stp->smbst_hdr->smbh_len) {
+ return (smb_set_errno(shp, ESMB_CORRUPT));
+ }
+
+ switch (proc->smbpai_type) {
+ case SMB_PROCINFO_T_RV32:
+ case SMB_PROCINFO_T_RV64:
+ case SMB_PROCINFO_T_RV128:
+ break;
+ default:
+ return (smb_set_errno(shp, ESMB_TYPE));
+ }
+
+ if (stp->smbst_hdr->smbh_len < sizeof (*proc) + sizeof (*rv)) {
+ return (smb_set_errno(shp, ESMB_SHORT));
+ }
+ rv = (const smb_processor_info_riscv_t *)&proc->smbpai_data[0];
+ if (rv->smbpairv_len != sizeof (*rv)) {
+ return (smb_set_errno(shp, ESMB_CORRUPT));
+ }
+
+ bcopy(rv->smbpairv_hartid, riscv->smbpirv_hartid,
+ sizeof (riscv->smbpirv_hartid));
+ bcopy(rv->smbpairv_vendid, riscv->smbpirv_vendid,
+ sizeof (riscv->smbpirv_vendid));
+ bcopy(rv->smbpairv_archid, riscv->smbpirv_archid,
+ sizeof (riscv->smbpirv_archid));
+ bcopy(rv->smbpairv_machid, riscv->smbpirv_machid,
+ sizeof (riscv->smbpirv_machid));
+ bcopy(rv->smbpairv_metdi, riscv->smbpirv_metdi,
+ sizeof (riscv->smbpirv_metdi));
+ bcopy(rv->smbpairv_mitdi, riscv->smbpirv_mitdi,
+ sizeof (riscv->smbpirv_mitdi));
+ riscv->smbpirv_isa = rv->smbpairv_isa;
+ riscv->smbpirv_privlvl = rv->smbpairv_privlvl;
+ riscv->smbpirv_boothart = rv->smbpairv_boot;
+ riscv->smbpirv_xlen = rv->smbpairv_xlen;
+ riscv->smbpirv_mxlen = rv->smbpairv_mxlen;
+ riscv->smbpirv_sxlen = rv->smbpairv_sxlen;
+ riscv->smbpirv_uxlen = rv->smbpairv_uxlen;
+
+ return (0);
+}
+
+int
+smbios_info_pointdev(smbios_hdl_t *shp, id_t id, smbios_pointdev_t *pd)
+{
+ const smb_struct_t *stp = smb_lookup_id(shp, id);
+ smb_pointdev_t point;
+
+ if (stp->smbst_hdr->smbh_type != SMB_TYPE_POINTDEV) {
+ return (smb_set_errno(shp, ESMB_TYPE));
+ }
+
+ if (stp->smbst_hdr->smbh_len < sizeof (point)) {
+ return (smb_set_errno(shp, ESMB_SHORT));
+ }
+
+ bzero(pd, sizeof (*pd));
+ smb_info_bcopy(stp->smbst_hdr, &point, sizeof (point));
+
+ pd->smbpd_type = point.smbpdev_type;
+ pd->smbpd_iface = point.smbpdev_iface;
+ pd->smbpd_nbuttons = point.smbpdev_nbuttons;
+
+ return (0);
+}
diff --git a/usr/src/common/smbios/smb_open.c b/usr/src/common/smbios/smb_open.c
index a81294592d..372b2b619b 100644
--- a/usr/src/common/smbios/smb_open.c
+++ b/usr/src/common/smbios/smb_open.c
@@ -230,6 +230,7 @@ smbios_bufopen(const smbios_entry_t *ep, const void *buf, size_t len,
case SMB_VERSION_30:
case SMB_VERSION_31:
case SMB_VERSION_32:
+ case SMB_VERSION_33:
break;
default:
return (smb_open_error(shp, errp, ESMB_VERSION));
diff --git a/usr/src/grub/grub-0.97/Makefile.solaris.defs b/usr/src/grub/grub-0.97/Makefile.solaris.defs
index 86e79c484b..4c9a5a81b0 100644
--- a/usr/src/grub/grub-0.97/Makefile.solaris.defs
+++ b/usr/src/grub/grub-0.97/Makefile.solaris.defs
@@ -44,9 +44,11 @@ OPTION_DOCS = $(POUND_SIGN)
OPTION_FS = $(POUND_SIGN)
-BASE_CFLAGS = -B$(GNUC_ROOT)/bin/ -g $(CPPFLAGS) $(OPTFLAGS) -std=gnu89
-BASE_CCASFLAGS = -B$(GNUC_ROOT)/bin/ -g $(CPPFLAGS) $(OPTFLAGS)
-BASE_LDFLAGS =
+BASE_CFLAGS = $($(MACH)_XARCH)
+BASE_CFLAGS += -B$(GNUC_ROOT)/bin/ -g $(CPPFLAGS) $(OPTFLAGS) -std=gnu89
+BASE_CCASFLAGS = $($(MACH)_XARCH)
+BASE_CCASFLAGS += -B$(GNUC_ROOT)/bin/ -g $(CPPFLAGS) $(OPTFLAGS)
+BASE_LDFLAGS = $($(MACH)_XARCH)
CC = $(GNUC_ROOT)/bin/gcc
CFLAGS = $(BASE_CFLAGS)
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 0167ee68a6..bc1de518c8 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -181,6 +181,7 @@ SUBDIRS += \
libofmt \
libpam \
libpcidb \
+ libpcsc \
libpctx \
libpicl \
libpicltree \
@@ -449,6 +450,7 @@ HDRSUBDIRS= \
libofmt \
libpam \
libpcidb \
+ libpcsc \
libpctx \
libpicl \
libpicltree \
diff --git a/usr/src/lib/cfgadm_plugins/Makefile b/usr/src/lib/cfgadm_plugins/Makefile
index 00d258b20a..8fa7f73df7 100644
--- a/usr/src/lib/cfgadm_plugins/Makefile
+++ b/usr/src/lib/cfgadm_plugins/Makefile
@@ -20,13 +20,14 @@
#
#
# Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
# lib/cfgadm_plugins/Makefile
#
include $(SRC)/Makefile.master
-COMMON_SUBDIRS= scsi pci usb ib fp shp sbd
+COMMON_SUBDIRS= scsi pci usb ib fp shp sbd ccid
sparc_SUBDIRS= ac sysctrl
i386_SUBDIRS= sata
@@ -37,7 +38,7 @@ ALL_SUBDIRS= $(COMMON_SUBDIRS) $(sparc_SUBDIRS) $(i386_SUBDIRS)
MSGSUBDIRS= $(ALL_SUBDIRS)
-all:= TARGET= all
+all:= TARGET= all
install:= TARGET= install
clean:= TARGET= clean
clobber:= TARGET= clobber
diff --git a/usr/src/lib/cfgadm_plugins/ccid/Makefile b/usr/src/lib/cfgadm_plugins/ccid/Makefile
new file mode 100644
index 0000000000..7d4af31c7d
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/ccid/Makefile
@@ -0,0 +1,67 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+include ../../Makefile.lib
+
+$(INTEL_BLD)SUBDIRS = $(MACH) $(BUILD64) $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+delete := TARGET= delete
+install := TARGET= install
+_msg := TARGET= _msg
+package := TARGET= package
+
+SED= sed
+GREP= grep
+
+.KEEP_STATE:
+
+all clean delete install package: $(SUBDIRS)
+clobber: $(SUBDIRS)
+ $(RM) $(POFILE) $(POFILES)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+#
+# We don't build any gettext libraries here
+#
+_msg:
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+$(POFILES):
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext */*.[ch]`
+ $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
+ $(RM) messages.po
+
+FRC:
diff --git a/usr/src/lib/cfgadm_plugins/ccid/Makefile.com b/usr/src/lib/cfgadm_plugins/ccid/Makefile.com
new file mode 100644
index 0000000000..30a2ce1527
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/ccid/Makefile.com
@@ -0,0 +1,67 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+LIBRARY= ccid.a
+VERS= .1
+
+OBJECTS= cfga_ccid.o
+
+# include library definitions
+include ../../../Makefile.lib
+
+SRCDIR = ../common
+ROOTLIBDIR= $(ROOT)/usr/lib/cfgadm
+ROOTLIBDIR64= $(ROOTLIBDIR)/$(MACH64)
+
+LIBS= $(DYNLIB)
+
+CFLAGS += $(CCVERBOSE)
+CFLAGS64 += $(CCVERBOSE)
+
+LDLIBS += -lc
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+# Install rules
+
+$(ROOTLIBDIR)/%: % $(ROOTLIBDIR)
+ $(INS.file)
+
+$(ROOTLIBDIR64)/%: % $(ROOTLIBDIR64)
+ $(INS.file)
+
+$(ROOTLIBDIR) $(ROOTLIBDIR64):
+ $(INS.dir)
+
+# include library targets
+include ../../../Makefile.targ
+
+objs/%.o pics/%.o: ../common/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/cfgadm_plugins/ccid/amd64/Makefile b/usr/src/lib/cfgadm_plugins/ccid/amd64/Makefile
new file mode 100644
index 0000000000..f2eb3c97a2
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/ccid/amd64/Makefile
@@ -0,0 +1,21 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/cfgadm_plugins/ccid/common/cfga_ccid.c b/usr/src/lib/cfgadm_plugins/ccid/common/cfga_ccid.c
new file mode 100644
index 0000000000..4ce28c2aec
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/ccid/common/cfga_ccid.c
@@ -0,0 +1,421 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * CCID cfgadm plugin
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+#define CFGA_PLUGIN_LIB
+#include <config_admin.h>
+
+int cfga_version = CFGA_HSL_V2;
+
+static cfga_err_t
+cfga_ccid_error(cfga_err_t err, char **errp, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (errp == NULL)
+ return (err);
+
+ /*
+ * Try to format a string. However because we have to return allocated
+ * memory, if this fails, then we have no error.
+ */
+ va_start(ap, fmt);
+ (void) vasprintf(errp, fmt, ap);
+ va_end(ap);
+
+ return (err);
+}
+
+cfga_err_t
+cfga_ccid_modify(uccid_cmd_icc_modify_t *modify, const char *ap,
+ struct cfga_confirm *confp, struct cfga_msg *msgp, char **errp,
+ boolean_t force)
+{
+ int fd;
+ uccid_cmd_status_t ucs;
+ uccid_cmd_txn_begin_t begin;
+ boolean_t held = B_FALSE;
+
+ /*
+ * Check ap is valid by doing a status request.
+ */
+ if ((fd = open(ap, O_RDWR)) < 0) {
+ return (cfga_ccid_error(CFGA_LIB_ERROR, errp,
+ "failed to open %s: %s", ap, strerror(errno)));
+ }
+
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+
+ if (ioctl(fd, UCCID_CMD_STATUS, &ucs) != 0) {
+ int e = errno;
+ if (errno == ENODEV) {
+ (void) close(fd);
+ return (cfga_ccid_error(CFGA_LIB_ERROR, errp,
+ "ap %s going away", ap));
+ }
+ (void) close(fd);
+ return (cfga_ccid_error(CFGA_ERROR, errp,
+ "ioctl on ap %s failed: %s", ap, strerror(e)));
+ }
+
+ /*
+ * Attempt to get a hold. If we cannot obtain a hold, we will not
+ * perform this unless the user has said we should force this.
+ */
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ begin.uct_flags = UCCID_TXN_DONT_BLOCK;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ if (errno != EBUSY) {
+ int e = errno;
+ (void) close(fd);
+ return (cfga_ccid_error(CFGA_ERROR, errp, "failed to "
+ "begin ccid transaction on ap %s: %s", ap,
+ strerror(e)));
+ }
+
+ /*
+ * If the user didn't force this operation, prompt if we would
+ * interfere.
+ */
+ if (!force) {
+ int confirm = 0;
+ const char *prompt = "CCID slot is held exclusively "
+ "by another program. Proceeding may interrupt "
+ "their functionality. Continue?";
+ if (confp != NULL && confp->appdata_ptr != NULL) {
+ confirm = (*confp->confirm)(confp->appdata_ptr,
+ prompt);
+ }
+
+ if (confirm == 0) {
+ (void) close(fd);
+ return (CFGA_NACK);
+ }
+ }
+ } else {
+ held = B_TRUE;
+ }
+
+ if (ioctl(fd, UCCID_CMD_ICC_MODIFY, modify) != 0) {
+ int e = errno;
+ (void) close(fd);
+ return (cfga_ccid_error(CFGA_ERROR, errp,
+ "failed to modify state on ap %s: %s", ap,
+ strerror(e)));
+ }
+
+ if (held) {
+ uccid_cmd_txn_end_t end;
+
+ bzero(&end, sizeof (end));
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ int e = errno;
+ (void) close(fd);
+ return (cfga_ccid_error(CFGA_ERROR, errp, "failed to "
+ "end transaction on ap %s: %s", ap,
+ strerror(e)));
+ }
+ }
+
+ (void) close(fd);
+ return (CFGA_OK);
+
+}
+
+cfga_err_t
+cfga_change_state(cfga_cmd_t cmd, const char *ap, const char *opts,
+ struct cfga_confirm *confp, struct cfga_msg *msgp, char **errp,
+ cfga_flags_t flags)
+{
+ uccid_cmd_icc_modify_t modify;
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ if (ap == NULL) {
+ return (cfga_ccid_error(CFGA_APID_NOEXIST, errp, NULL));
+ }
+
+ if (opts != NULL) {
+ return (cfga_ccid_error(CFGA_ERROR, errp,
+ "hardware specific options are not supported"));
+ }
+
+ bzero(&modify, sizeof (modify));
+ modify.uci_version = UCCID_CURRENT_VERSION;
+ switch (cmd) {
+ case CFGA_CMD_CONFIGURE:
+ modify.uci_action = UCCID_ICC_POWER_ON;
+ break;
+ case CFGA_CMD_UNCONFIGURE:
+ modify.uci_action = UCCID_ICC_POWER_OFF;
+ break;
+ default:
+ (void) cfga_help(msgp, opts, flags);
+ return (CFGA_OPNOTSUPP);
+ }
+
+ return (cfga_ccid_modify(&modify, ap, confp, msgp, errp,
+ (flags & CFGA_FLAG_FORCE) != 0));
+}
+
+cfga_err_t
+cfga_private_func(const char *function, const char *ap, const char *opts,
+ struct cfga_confirm *confp, struct cfga_msg *msgp, char **errp,
+ cfga_flags_t flags)
+{
+ uccid_cmd_icc_modify_t modify;
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ if (function == NULL) {
+ return (CFGA_ERROR);
+ }
+
+ if (ap == NULL) {
+ return (cfga_ccid_error(CFGA_APID_NOEXIST, errp, NULL));
+ }
+
+ if (opts != NULL) {
+ return (cfga_ccid_error(CFGA_ERROR, errp,
+ "hardware specific options are not supported"));
+ }
+
+ if (strcmp(function, "warm_reset") != 0) {
+ return (CFGA_OPNOTSUPP);
+ }
+
+ bzero(&modify, sizeof (modify));
+ modify.uci_version = UCCID_CURRENT_VERSION;
+ modify.uci_action = UCCID_ICC_WARM_RESET;
+
+ return (cfga_ccid_modify(&modify, ap, confp, msgp, errp,
+ (flags & CFGA_FLAG_FORCE) != 0));
+}
+
+/*
+ * We don't support the test entry point for CCID.
+ */
+cfga_err_t
+cfga_test(const char *ap, const char *opts, struct cfga_msg *msgp, char **errp,
+ cfga_flags_t flags)
+{
+ (void) cfga_help(msgp, opts, flags);
+ return (CFGA_OPNOTSUPP);
+}
+
+static void
+cfga_ccid_fill_info(const uccid_cmd_status_t *ucs, char *buf, size_t len)
+{
+ const char *product, *serial, *tran, *prot;
+ uint_t bits = CCID_CLASS_F_TPDU_XCHG | CCID_CLASS_F_SHORT_APDU_XCHG |
+ CCID_CLASS_F_EXT_APDU_XCHG;
+
+ if ((ucs->ucs_status & UCCID_STATUS_F_PRODUCT_VALID) != 0) {
+ product = ucs->ucs_product;
+ } else {
+ product = "<unknown>";
+ }
+
+ if ((ucs->ucs_status & UCCID_STATUS_F_SERIAL_VALID) != 0) {
+ serial = ucs->ucs_serial;
+ } else {
+ serial = "<unknown>";
+ }
+
+ switch (ucs->ucs_class.ccd_dwFeatures & bits) {
+ case 0:
+ tran = "Character";
+ break;
+ case CCID_CLASS_F_TPDU_XCHG:
+ tran = "TPDU";
+ break;
+ case CCID_CLASS_F_SHORT_APDU_XCHG:
+ case CCID_CLASS_F_EXT_APDU_XCHG:
+ tran = "APDU";
+ break;
+ default:
+ tran = "Unknown";
+ break;
+ }
+
+ if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) != 0) {
+ switch (ucs->ucs_prot) {
+ case UCCID_PROT_T0:
+ prot = " (T=0)";
+ break;
+ case UCCID_PROT_T1:
+ prot = " (T=1)";
+ break;
+ default:
+ prot = "<unknown>";
+ break;
+ }
+ } else {
+ prot = "<unknown>";
+ }
+
+ if ((ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE) != 0) {
+ (void) snprintf(buf, len, "Product: %s Serial: %s "
+ "Transport: %s Protocol: %s", product, serial,
+ tran, prot);
+ } else {
+ (void) snprintf(buf, len, "Product: %s Serial: %s ",
+ product, serial);
+ }
+}
+
+cfga_err_t
+cfga_list_ext(const char *ap, struct cfga_list_data **ap_list, int *nlist,
+ const char *opts, const char *listopts, char **errp, cfga_flags_t flags)
+{
+ int fd;
+ uccid_cmd_status_t ucs;
+ struct cfga_list_data *cld;
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ if (ap == NULL) {
+ return (cfga_ccid_error(CFGA_APID_NOEXIST, errp, NULL));
+ }
+
+ if (opts != NULL) {
+ return (cfga_ccid_error(CFGA_ERROR, errp,
+ "hardware specific options are not supported"));
+ }
+
+ if ((fd = open(ap, O_RDWR)) < 0) {
+ return (cfga_ccid_error(CFGA_LIB_ERROR, errp,
+ "failed to open %s: %s", ap, strerror(errno)));
+ }
+
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+
+ if (ioctl(fd, UCCID_CMD_STATUS, &ucs) != 0) {
+ int e = errno;
+ (void) close(fd);
+ if (e == ENODEV) {
+ return (cfga_ccid_error(CFGA_LIB_ERROR, errp,
+ "ap %s going away", ap));
+ }
+ return (cfga_ccid_error(CFGA_ERROR, errp,
+ "ioctl on ap %s failed: %s", ap, strerror(e)));
+ }
+ (void) close(fd);
+
+ if ((cld = calloc(1, sizeof (*cld))) == NULL) {
+ return (cfga_ccid_error(CFGA_LIB_ERROR, errp, "failed to "
+ "allocate memory for list entry"));
+ }
+
+ if (snprintf(cld->ap_log_id, sizeof (cld->ap_log_id), "ccid%d/slot%u",
+ ucs.ucs_instance, ucs.ucs_slot) >= sizeof (cld->ap_log_id)) {
+ free(cld);
+ return (cfga_ccid_error(CFGA_LIB_ERROR, errp, "ap %s logical id"
+ " was too large", ap));
+ }
+
+ if (strlcpy(cld->ap_phys_id, ap, sizeof (cld->ap_phys_id)) >=
+ sizeof (cld->ap_phys_id)) {
+ free(cld);
+ return (cfga_ccid_error(CFGA_LIB_ERROR, errp,
+ "ap %s physical id was too long", ap));
+ }
+
+ cld->ap_class[0] = '\0';
+
+ if ((ucs.ucs_status & UCCID_STATUS_F_CARD_PRESENT) != 0) {
+ cld->ap_r_state = CFGA_STAT_CONNECTED;
+ if ((ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE) != 0) {
+ cld->ap_o_state = CFGA_STAT_CONFIGURED;
+ } else {
+ cld->ap_o_state = CFGA_STAT_UNCONFIGURED;
+ }
+ } else {
+ cld->ap_r_state = CFGA_STAT_EMPTY;
+ cld->ap_o_state = CFGA_STAT_UNCONFIGURED;
+ }
+
+ /*
+ * XXX We should probably have a way to indicate that there's an error
+ * when the ICC is basically foobar'd. We should also allow the status
+ * ioctl to know that the slot is resetting or something else is going
+ * on I guess.
+ */
+ if ((ucs.ucs_class.ccd_dwFeatures &
+ (CCID_CLASS_F_SHORT_APDU_XCHG | CCID_CLASS_F_EXT_APDU_XCHG)) == 0) {
+ cld->ap_cond = CFGA_COND_UNUSABLE;
+ } else {
+ cld->ap_cond = CFGA_COND_OK;
+ }
+ cld->ap_busy = 0;
+ cld->ap_status_time = (time_t)-1;
+ cfga_ccid_fill_info(&ucs, cld->ap_info, sizeof (cld->ap_info));
+ if (strlcpy(cld->ap_type, "icc", sizeof (cld->ap_type)) >=
+ sizeof (cld->ap_type)) {
+ free(cld);
+ return (cfga_ccid_error(CFGA_LIB_ERROR, errp,
+ "ap %s type overflowed ICC field", ap));
+ }
+
+ *ap_list = cld;
+ *nlist = 1;
+ return (CFGA_OK);
+}
+
+cfga_err_t
+cfga_help(struct cfga_msg *msgp, const char *opts, cfga_flags_t flags)
+{
+ (void) (*msgp->message_routine)(msgp, "CCID specific commands:\n");
+ (void) (*msgp->message_routine)(msgp,
+ " cfgadm -c [configure|unconfigure] ap_id [ap_id...]\n");
+ (void) (*msgp->message_routine)(msgp,
+ " cfgadm -x warm_reset ap_id [ap_id...]\n");
+
+ return (CFGA_OK);
+}
+
+int
+cfga_ap_id_cmp(const cfga_ap_log_id_t ap_id1, const cfga_ap_log_id_t ap_id2)
+{
+ return (strcmp(ap_id1, ap_id2));
+}
diff --git a/usr/src/lib/cfgadm_plugins/ccid/common/mapfile-vers b/usr/src/lib/cfgadm_plugins/ccid/common/mapfile-vers
new file mode 100644
index 0000000000..7c7f7d2c0e
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/ccid/common/mapfile-vers
@@ -0,0 +1,42 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate_1.1 {
+ global:
+ cfga_change_state;
+ cfga_help;
+ cfga_list_ext;
+ cfga_private_func;
+ cfga_test;
+ cfga_version;
+ local:
+ *;
+};
diff --git a/usr/src/lib/cfgadm_plugins/ccid/i386/Makefile b/usr/src/lib/cfgadm_plugins/ccid/i386/Makefile
new file mode 100644
index 0000000000..66d7a51776
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/ccid/i386/Makefile
@@ -0,0 +1,20 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c
index 1c73c959ea..3211e65dfc 100644
--- a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c
@@ -1615,12 +1615,13 @@ uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data,
if (page_size < sizeof (scsi_log_header_t))
return (-1);
- log_sense_buf = alloca((uint_t)page_size);
+ log_sense_buf = calloc(1, page_size);
+ if (log_sense_buf == NULL)
+ return (-1);
/*
* Build and execute the uscsi ioctl
*/
- (void) memset(log_sense_buf, 0, page_size);
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_LOG_SENSE_G1;
@@ -1633,6 +1634,7 @@ uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data,
status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
if (status) {
dprintf("Log sense page 0x%x failed\n", page_code);
+ free(log_sense_buf);
return (-1);
}
@@ -1651,6 +1653,7 @@ uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data,
dprintf("\nLog sense page 0x%x: incorrect page code 0x%x\n",
page_code, hdr->lh_code);
ddump("Log sense:", log_sense_buf, page_size);
+ free(log_sense_buf);
return (-1);
}
@@ -1672,6 +1675,7 @@ uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data,
sizeof (scsi_log_header_t));
ddump("data:", (caddr_t)hdr +
sizeof (scsi_log_header_t), len);
+ free(log_sense_buf);
return (0);
}
diff --git a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
index 290b5af43c..8904c17ac5 100644
--- a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
+++ b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
@@ -122,6 +122,7 @@ SYMBOL_VERSION SUNWprivate {
topo_mod_nvl2str;
topo_mod_pcidb;
topo_mod_pkgfmri;
+ topo_mod_product;
topo_mod_prominfo;
topo_mod_register;
topo_mod_setdebug;
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
index 851f0264cd..1d57219c93 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
@@ -768,7 +768,7 @@ topo_mod_dprintf(topo_mod_t *mod, const char *format, ...)
va_end(alist);
}
-static char *
+char *
topo_mod_product(topo_mod_t *mod)
{
return (topo_mod_strdup(mod, mod->tm_hdl->th_product));
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
index bdf8b46c8d..8f4a68c428 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
@@ -81,6 +81,7 @@ extern int topo_mod_enumerate(topo_mod_t *, tnode_t *, const char *,
const char *, topo_instance_t, topo_instance_t, void *);
extern int topo_mod_enummap(topo_mod_t *mod, tnode_t *, const char *,
const char *);
+extern char *topo_mod_product(topo_mod_t *);
extern void topo_mod_release(topo_mod_t *, tnode_t *);
extern void topo_mod_setspecific(topo_mod_t *, void *);
extern void *topo_mod_getspecific(topo_mod_t *);
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.map b/usr/src/lib/fm/topo/libtopo/common/topo_mod.map
index 91ed5e4606..b3652fcf49 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.map
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.map
@@ -68,6 +68,7 @@ SYMBOL_SCOPE {
topo_mod_auth { TYPE = FUNCTION; FLAGS = extern };
topo_mod_clean_str { TYPE = FUNCTION; FLAGS = extern };
topo_mod_hc_occupied { TYPE = FUNCTION; FLAGS = extern };
+ topo_mod_product { TYPE = FUNCTION; FLAGS = extern };
topo_mod_walk_init { TYPE = FUNCTION; FLAGS = extern };
diff --git a/usr/src/lib/fm/topo/modules/common/ses/ses.c b/usr/src/lib/fm/topo/modules/common/ses/ses.c
index 627780b447..171de81f4a 100644
--- a/usr/src/lib/fm/topo/modules/common/ses/ses.c
+++ b/usr/src/lib/fm/topo/modules/common/ses/ses.c
@@ -23,7 +23,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright (c) 2020, Joyent, Inc.
*/
#include <alloca.h>
@@ -264,12 +264,22 @@ static const topo_method_t ses_component_methods[] = {
{ NULL }
};
+#define TOPO_METH_SMCI_4U36_LABEL "smci_4u36_bay_label"
+#define TOPO_METH_SMCI_4U36_LABEL_DESC \
+ "compute bay labels on SMCI 4U36 storage platform variants"
+#define TOPO_METH_SMCI_4U36_LABEL_VERSION 0
+static int smci_4u36_bay_label(topo_mod_t *, tnode_t *, topo_version_t,
+ nvlist_t *, nvlist_t **);
+
static const topo_method_t ses_bay_methods[] = {
{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
{ TOPO_METH_OCCUPIED, TOPO_METH_OCCUPIED_DESC,
TOPO_METH_OCCUPIED_VERSION, TOPO_STABILITY_INTERNAL,
topo_mod_hc_occupied },
+ { TOPO_METH_SMCI_4U36_LABEL, TOPO_METH_SMCI_4U36_LABEL_DESC,
+ TOPO_METH_SMCI_4U36_LABEL_VERSION, TOPO_STABILITY_INTERNAL,
+ smci_4u36_bay_label },
{ NULL }
};
@@ -289,6 +299,39 @@ static const topo_method_t ses_enclosure_methods[] = {
};
/*
+ * The bay_label_overrides table can be used to map a server product ID to a
+ * topo method that will be invoked to override the value of the label property
+ * for all bay nodes. By default the property value is static, derived from
+ * the corresponding SES array device element's descriptor string.
+ */
+typedef struct ses_label_overrides {
+ const char *slbl_product;
+ const char *slbl_mname;
+} ses_label_overrides_t;
+
+/*
+ * This table covers three generations of SMCI's 4U 36-bay storage server
+ * (and the Joyent-branded versions). There was also an Ivy Bridge variant
+ * which has been omitted due to an inability to find one to test on.
+ */
+static const ses_label_overrides_t bay_label_overrides[] = {
+ /* Sandy Bridge variant */
+ { "SSG-6047R-E1R36L", TOPO_METH_SMCI_4U36_LABEL },
+ { "Joyent-Storage-Platform-5001", TOPO_METH_SMCI_4U36_LABEL },
+
+ /* Broadwell variant */
+ { "SSG-6048R-E1CR36L", TOPO_METH_SMCI_4U36_LABEL },
+ { "Joyent-Storage-Platform-7001", TOPO_METH_SMCI_4U36_LABEL },
+
+ /* Skylake variant */
+ { "SSG-6049P-E1CR36L", TOPO_METH_SMCI_4U36_LABEL },
+ { "Joyent-S10G5", TOPO_METH_SMCI_4U36_LABEL }
+};
+
+#define N_BAY_LBL_OVERRIDES (sizeof (bay_label_overrides) / \
+ sizeof (bay_label_overrides[0]))
+
+/*
* Functions for tracking ses devices which we were unable to open. We retry
* these at regular intervals using ses_recheck_dir() and if we find that we
* can now open any of them then we send a sysevent to indicate that a new topo
@@ -1416,6 +1459,18 @@ error:
return (err);
}
+static const char *
+lookup_bay_override(const char *product_id)
+{
+ for (uint_t i = 0; i < N_BAY_LBL_OVERRIDES; i++) {
+ if (strcmp(product_id,
+ bay_label_overrides[i].slbl_product) == 0) {
+ return (bay_label_overrides[i].slbl_mname);
+ }
+ }
+ return (NULL);
+}
+
/*
* Callback to create a basic node (bay, psu, fan, or controller and expander).
*/
@@ -1520,10 +1575,11 @@ ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, tnode_t *pnode,
goto error;
if (strcmp(nodename, BAY) == 0) {
- if (ses_add_bay_props(mod, tn, snp) != 0)
- goto error;
+ const char *label_method;
+ char *product;
+ nvlist_t *args = NULL;
- if (ses_create_disk(sdp, tn, props) != 0)
+ if (ses_add_bay_props(mod, tn, snp) != 0)
goto error;
if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
@@ -1532,6 +1588,39 @@ ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, tnode_t *pnode,
topo_mod_errmsg(mod));
goto error;
}
+
+ /*
+ * Ideally we'd perform this sort of override with a platform
+ * specific XML map file, and that would work here if we only
+ * wanted to override the bay node label. However, we'd also
+ * like the disk node label (if the bay is occupied) to inherit
+ * the overriden bay label. So we need to ensure the
+ * propmethod is registered before we create the child disk
+ * node.
+ */
+ if ((product = topo_mod_product(mod)) == NULL) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto error;
+ }
+ if ((label_method = lookup_bay_override(product)) != NULL) {
+ if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
+ topo_prop_method_register(tn, TOPO_PGROUP_PROTOCOL,
+ TOPO_PROP_LABEL, TOPO_TYPE_STRING, label_method,
+ args, &err)) {
+ topo_mod_dprintf(mod,
+ "Failed to register method: %s on %s=%"
+ PRIu64, label_method, BAY,
+ topo_node_instance(tn));
+ topo_mod_strfree(mod, product);
+ nvlist_free(args);
+ goto error;
+ }
+ nvlist_free(args);
+ }
+ topo_mod_strfree(mod, product);
+
+ if (ses_create_disk(sdp, tn, props) != 0)
+ goto error;
} else if ((strcmp(nodename, FAN) == 0) ||
(strcmp(nodename, PSU) == 0) ||
(strcmp(nodename, CONTROLLER) == 0)) {
@@ -3637,6 +3726,92 @@ error:
return (err);
}
+/*
+ * Different generations of SMCI's 4U36 storage servers used different models
+ * of front and rear SAS expanders.
+ */
+#define SMCI4U36_FRONT_EXPANDER_PID1 "LSI-SAS2X36"
+#define SMCI4U36_FRONT_EXPANDER_PID2 "LSI-SAS3x40"
+#define SMCI4U36_FRONT_EXPANDER_PID3 "SMC-SC846P"
+
+#define SMCI4U36_REAR_EXPANDER_PID1 "LSI-CORP-SAS2X28"
+#define SMCI4U36_REAR_EXPANDER_PID2 "LSI-SAS3x28"
+
+static int
+smci_4u36_bay_label(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+ nvlist_t *in, nvlist_t **out)
+{
+ int err, ret = -1;
+ nvlist_t *pargs, *auth, *nvl = NULL, *fmri;
+ char *label = NULL, *product_id;
+
+ /*
+ * Now look for a private argument list to determine if the invoker is
+ * trying to do a set operation and if so, return an error as this
+ * method only supports get operations.
+ */
+ if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
+ nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
+ topo_mod_dprintf(mod, "%s: set operation not suppported",
+ __func__);
+ return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
+ }
+
+ if (topo_node_resource(node, &fmri, &err) != 0) {
+ (void) topo_mod_seterrno(mod, err);
+ goto err;
+ }
+
+ if (nvlist_lookup_nvlist(fmri, FM_FMRI_AUTHORITY, &auth) != 0 ||
+ nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, &product_id) !=
+ 0) {
+ topo_mod_dprintf(mod, "%s: malformed FMRI", __func__);
+ (void) topo_mod_seterrno(mod, EMOD_UNKNOWN);
+ nvlist_free(fmri);
+ goto err;
+ }
+ nvlist_free(fmri);
+
+ if (strcmp(product_id, SMCI4U36_FRONT_EXPANDER_PID1) == 0 ||
+ strcmp(product_id, SMCI4U36_FRONT_EXPANDER_PID2) == 0 ||
+ strcmp(product_id, SMCI4U36_FRONT_EXPANDER_PID3) == 0) {
+ err = asprintf(&label, "Front Slot %" PRIu64,
+ topo_node_instance(node));
+ } else if (strcmp(product_id, SMCI4U36_REAR_EXPANDER_PID1) == 0 ||
+ strcmp(product_id, SMCI4U36_REAR_EXPANDER_PID2) == 0) {
+ err = asprintf(&label, "Rear Slot %" PRIu64,
+ topo_node_instance(node));
+ } else {
+ topo_mod_dprintf(mod, "%s: unexpected expander product id: %s",
+ __func__, product_id);
+ (void) topo_mod_seterrno(mod, EMOD_UNKNOWN);
+ goto err;
+ }
+
+ if (err < 0) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+
+ if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
+ nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_PROP_LABEL) != 0 ||
+ nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING)
+ != 0 ||
+ nvlist_add_string(nvl, TOPO_PROP_VAL_VAL, label)
+ != 0) {
+ topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist");
+ nvlist_free(nvl);
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+ *out = nvl;
+ ret = 0;
+err:
+ free(label);
+ return (ret);
+
+}
+
static void
ses_release(topo_mod_t *mod, tnode_t *tn)
{
diff --git a/usr/src/lib/libbe/common/be_create.c b/usr/src/lib/libbe/common/be_create.c
index 791d678302..4c69ac38f6 100644
--- a/usr/src/lib/libbe/common/be_create.c
+++ b/usr/src/lib/libbe/common/be_create.c
@@ -24,6 +24,7 @@
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, 2015 by Delphix. All rights reserved.
* Copyright (c) 2016 Martin Matuska. All rights reserved.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
*/
/*
@@ -611,6 +612,8 @@ be_copy(nvlist_t *be_attrs)
uuid_t parent_uu = { 0 };
char obe_root_ds[MAXPATHLEN];
char nbe_root_ds[MAXPATHLEN];
+ char obe_root_container[MAXPATHLEN];
+ char nbe_root_container[MAXPATHLEN];
char ss[MAXPATHLEN];
char *new_mp = NULL;
char *obe_name = NULL;
@@ -1095,8 +1098,14 @@ be_copy(nvlist_t *be_attrs)
/*
* Update new BE's vfstab.
*/
- if ((ret = be_update_vfstab(bt.nbe_name, bt.obe_zpool, bt.nbe_zpool,
- &fld, new_mp)) != BE_SUCCESS) {
+
+ be_make_root_container_ds(bt.obe_zpool, obe_root_container,
+ sizeof (obe_root_container));
+ be_make_root_container_ds(bt.nbe_zpool, nbe_root_container,
+ sizeof (nbe_root_container));
+
+ if ((ret = be_update_vfstab(bt.nbe_name, obe_root_container,
+ nbe_root_container, &fld, new_mp)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to "
"update new BE's vfstab (%s)\n"), bt.nbe_name);
goto done;
diff --git a/usr/src/lib/libbe/common/be_rename.c b/usr/src/lib/libbe/common/be_rename.c
index dee131834c..64c26943bf 100644
--- a/usr/src/lib/libbe/common/be_rename.c
+++ b/usr/src/lib/libbe/common/be_rename.c
@@ -69,6 +69,7 @@ be_rename(nvlist_t *be_attrs)
be_fs_list_data_t fld = { 0 };
zfs_handle_t *zhp = NULL;
char root_ds[MAXPATHLEN];
+ char be_root_container[MAXPATHLEN];
char *mp = NULL;
int zret = 0, ret = BE_SUCCESS;
@@ -205,8 +206,16 @@ be_rename(nvlist_t *be_attrs)
}
/* Update BE's vfstab */
- if ((ret = be_update_vfstab(bt.nbe_name, bt.obe_zpool, bt.nbe_zpool,
- &fld, mp)) != BE_SUCCESS) {
+
+ /*
+ * Since the new and old BEs reside in the same pool (see above),
+ * the same variable can be used for the container for both.
+ */
+ be_make_root_container_ds(bt.obe_zpool, be_root_container,
+ sizeof (be_root_container));
+
+ if ((ret = be_update_vfstab(bt.nbe_name, be_root_container,
+ be_root_container, &fld, mp)) != BE_SUCCESS) {
be_print_err(gettext("be_rename: "
"failed to update new BE's vfstab (%s)\n"), bt.nbe_name);
goto done;
diff --git a/usr/src/lib/libbe/common/be_utils.c b/usr/src/lib/libbe/common/be_utils.c
index 72b41b3315..f748ad978f 100644
--- a/usr/src/lib/libbe/common/be_utils.c
+++ b/usr/src/lib/libbe/common/be_utils.c
@@ -24,7 +24,7 @@
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
* Copyright 2016 Toomas Soome <tsoome@me.com>
* Copyright (c) 2015 by Delphix. All rights reserved.
- * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
* Copyright (c) 2018, Joyent, Inc.
*/
@@ -517,6 +517,35 @@ be_make_container_ds(const char *zpool, char *container_ds,
}
/*
+ * Function: be_make_root_container_ds
+ * Description: Generate string for the BE root container dataset given a pool
+ * name.
+ * Parameters:
+ * zpool - pointer zpool name.
+ * container_ds - pointer to buffer in which to return result
+ * container_ds_size - size of container_ds
+ * Returns:
+ * None
+ * Scope:
+ * Semi-private (library wide use only)
+ */
+void
+be_make_root_container_ds(const char *zpool, char *container_ds,
+ int container_ds_size)
+{
+ char *root;
+
+ be_make_container_ds(zpool, container_ds, container_ds_size);
+
+ /* If the container DS ends with /ROOT, remove it. */
+
+ if ((root = strrchr(container_ds, '/')) != NULL &&
+ strcmp(root + 1, BE_CONTAINER_DS_NAME) == 0) {
+ *root = '\0';
+ }
+}
+
+/*
* Function: be_make_name_from_ds
* Description: This function takes a dataset name and strips off the
* BE container dataset portion from the beginning. The
diff --git a/usr/src/lib/libbe/common/libbe_priv.h b/usr/src/lib/libbe/common/libbe_priv.h
index cbd382242b..f2960c4f17 100644
--- a/usr/src/lib/libbe/common/libbe_priv.h
+++ b/usr/src/lib/libbe/common/libbe_priv.h
@@ -24,7 +24,7 @@
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
* Copyright 2016 Toomas Soome <tsoome@me.com>
* Copyright (c) 2015 by Delphix. All rights reserved.
- * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
*/
#ifndef _LIBBE_PRIV_H
@@ -175,6 +175,7 @@ boolean_t be_zfs_init(void);
void be_zfs_fini(void);
void be_make_root_ds(const char *, const char *, char *, int);
void be_make_container_ds(const char *, char *, int);
+void be_make_root_container_ds(const char *, char *, int);
char *be_make_name_from_ds(const char *, char *);
int be_append_menu(char *, char *, char *, char *, char *);
int be_remove_menu(char *, char *, char *);
diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com
index 5b51713a06..9987e2ee87 100644
--- a/usr/src/lib/libc/i386/Makefile.com
+++ b/usr/src/lib/libc/i386/Makefile.com
@@ -1279,8 +1279,8 @@ $(LIB_PIC): pics $$(PICS)
$(POST_PROCESS_A)
$(LIBCBASE)/crt/_rtbootld.s: $(LIBCBASE)/crt/_rtboot.s $(LIBCBASE)/crt/_rtld.c
- $(CC) $(CPPFLAGS) -_smatch=off $(CTF_FLAGS) -O -S $(C_PICFLAGS) \
- $(LIBCBASE)/crt/_rtld.c -o $(LIBCBASE)/crt/_rtld.s
+ $(CC) $($(MACH)_XARCH) $(CPPFLAGS) -_smatch=off $(CTF_FLAGS) -O -S \
+ $(C_PICFLAGS) $(LIBCBASE)/crt/_rtld.c -o $(LIBCBASE)/crt/_rtld.s
$(CAT) $(LIBCBASE)/crt/_rtboot.s $(LIBCBASE)/crt/_rtld.s > $@
$(RM) $(LIBCBASE)/crt/_rtld.s
diff --git a/usr/src/lib/libcmdutils/common/nicenum.c b/usr/src/lib/libcmdutils/common/nicenum.c
index 8e3202f792..9b9a8fcd5e 100644
--- a/usr/src/lib/libcmdutils/common/nicenum.c
+++ b/usr/src/lib/libcmdutils/common/nicenum.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2017 Jason king
+ * Copyright 2019, Joyent, Inc.
*/
#include <stdio.h>
@@ -44,8 +45,15 @@ nicenum_scale(uint64_t n, size_t units, char *buf, size_t buflen,
uint64_t divisor = 1;
int index = 0;
int rc = 0;
+ int spclen = 0;
+ char *spc = "";
char u;
+ if (flags & NN_UNIT_SPACE) {
+ spc = " ";
+ spclen = 1;
+ }
+
if (units == 0)
units = 1;
@@ -90,7 +98,7 @@ nicenum_scale(uint64_t n, size_t units, char *buf, size_t buflen,
* If this is an even multiple of the base, always display
* without any decimal precision.
*/
- rc = snprintf(buf, buflen, "%llu%c", n / divisor, u);
+ rc = snprintf(buf, buflen, "%llu%s%c", n / divisor, spc, u);
} else {
/*
* We want to choose a precision that reflects the best choice
@@ -104,8 +112,8 @@ nicenum_scale(uint64_t n, size_t units, char *buf, size_t buflen,
*/
int i;
for (i = 2; i >= 0; i--) {
- if ((rc = snprintf(buf, buflen, "%.*f%c", i,
- (double)n / divisor, u)) <= 5)
+ if ((rc = snprintf(buf, buflen, "%.*f%s%c", i,
+ (double)n / divisor, spc, u)) <= 5 + spclen)
break;
}
}
diff --git a/usr/src/lib/libcmdutils/libcmdutils.h b/usr/src/lib/libcmdutils/libcmdutils.h
index c9a61aab4d..54c025c9d9 100644
--- a/usr/src/lib/libcmdutils/libcmdutils.h
+++ b/usr/src/lib/libcmdutils/libcmdutils.h
@@ -26,7 +26,7 @@
* Copyright (c) 2013 RackTop Systems.
*/
/*
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -163,6 +163,7 @@ extern int findnextuid(uid_t, uid_t, uid_t *);
extern int findnextgid(gid_t, gid_t, gid_t *);
#define NN_DIVISOR_1000 (1U << 0)
+#define NN_UNIT_SPACE (1U << 1)
/* Minimum size for the output of nicenum, including NULL */
#define NN_NUMBUF_SZ (6)
diff --git a/usr/src/lib/libdtrace/common/dt_options.c b/usr/src/lib/libdtrace/common/dt_options.c
index be985d6dab..991f197304 100644
--- a/usr/src/lib/libdtrace/common/dt_options.c
+++ b/usr/src/lib/libdtrace/common/dt_options.c
@@ -700,7 +700,7 @@ dt_opt_rate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
char *name;
hrtime_t mul;
} suffix[] = {
- { "ns", NANOSEC / NANOSEC },
+ { "ns", NANOSEC / NANOSEC },
{ "nsec", NANOSEC / NANOSEC },
{ "us", NANOSEC / MICROSEC },
{ "usec", NANOSEC / MICROSEC },
@@ -915,15 +915,19 @@ dt_options_load(dtrace_hdl_t *dtp)
if (hdr.dofh_loadsz < sizeof (dof_hdr_t))
return (dt_set_errno(dtp, EINVAL));
- dof = alloca(hdr.dofh_loadsz);
- bzero(dof, sizeof (dof_hdr_t));
+ dof = calloc(1, hdr.dofh_loadsz);
+ if (dof == NULL)
+ return (dt_set_errno(dtp, errno));
+
dof->dofh_loadsz = hdr.dofh_loadsz;
for (i = 0; i < DTRACEOPT_MAX; i++)
dtp->dt_options[i] = DTRACEOPT_UNSET;
- if (dt_ioctl(dtp, DTRACEIOC_DOFGET, dof) == -1)
+ if (dt_ioctl(dtp, DTRACEIOC_DOFGET, dof) == -1) {
+ free(dof);
return (dt_set_errno(dtp, errno));
+ }
for (i = 0; i < dof->dofh_secnum; i++) {
sec = (dof_sec_t *)(uintptr_t)((uintptr_t)dof +
@@ -948,6 +952,7 @@ dt_options_load(dtrace_hdl_t *dtp)
dtp->dt_options[opt->dofo_option] = opt->dofo_value;
}
+ free(dof);
return (0);
}
diff --git a/usr/src/lib/libmail/common/notifyu.c b/usr/src/lib/libmail/common/notifyu.c
index 9d4b7d4694..1cbeb583fc 100644
--- a/usr/src/lib/libmail/common/notifyu.c
+++ b/usr/src/lib/libmail/common/notifyu.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#include "libmail.h"
#include <sys/types.h>
@@ -41,12 +39,10 @@ typedef void (*SIG_PF) (int);
#include <unistd.h>
#include <signal.h>
-static SIG_PF catcher(void);
-
-static SIG_PF catcher(void)
+static void
+catcher(int arg __unused)
{
/* do nothing, but allow the write() to break */
- return (0);
}
void
@@ -77,7 +73,7 @@ notify(char *user, char *msg, int check_mesg_y, char *etcdir)
(void) sprintf(dev, "%s/dev/%s", etcdir, tty);
/* break out if write() to the tty hangs */
- old = (SIG_PF)signal(SIGALRM, (SIG_PF)catcher);
+ old = (SIG_PF)signal(SIGALRM, catcher);
oldalarm = alarm(300);
/* check if device is really a tty */
diff --git a/usr/src/lib/libpcsc/Makefile b/usr/src/lib/libpcsc/Makefile
new file mode 100644
index 0000000000..b5fdecb2e4
--- /dev/null
+++ b/usr/src/lib/libpcsc/Makefile
@@ -0,0 +1,43 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+include ../Makefile.lib
+
+HDRS = wintypes.h winscard.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+
+.KEEP_STATE:
+
+all clean clobber: $(SUBDIRS)
+
+install: $(SUBDIRS) install_h
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libpcsc/Makefile.com b/usr/src/lib/libpcsc/Makefile.com
new file mode 100644
index 0000000000..7bc91ef667
--- /dev/null
+++ b/usr/src/lib/libpcsc/Makefile.com
@@ -0,0 +1,32 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+LIBRARY = libpcsc.a
+VERS = .1
+OBJECTS = libpcsc.o
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB)
+LDLIBS += -lc
+CPPFLAGS += -I../common
+
+SRCDIR = ../common
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libpcsc/amd64/Makefile b/usr/src/lib/libpcsc/amd64/Makefile
new file mode 100644
index 0000000000..a32567f965
--- /dev/null
+++ b/usr/src/lib/libpcsc/amd64/Makefile
@@ -0,0 +1,19 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libpcsc/common/libpcsc.c b/usr/src/lib/libpcsc/common/libpcsc.c
new file mode 100644
index 0000000000..41d646e8b1
--- /dev/null
+++ b/usr/src/lib/libpcsc/common/libpcsc.c
@@ -0,0 +1,615 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <errno.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/debug.h>
+#include <sys/filio.h>
+#include <sys/usb/clients/ccid/uccid.h>
+
+#include <winscard.h>
+
+/*
+ * Implementation of the PCSC library leveraging the uccid framework.
+ */
+
+/*
+ * The library handle is basically unused today. We keep this around such that
+ * consumers which expect to receive a non-NULL opaque handle have something
+ * they can use.
+ */
+typedef struct pcsc_hdl {
+ hrtime_t pcsc_create_time;
+} pcsc_hdl_t;
+
+typedef struct pcsc_card {
+ int pcc_fd;
+} pcsc_card_t;
+
+/*
+ * Required globals
+ */
+SCARD_IO_REQUEST g_rgSCardT0Pci = {
+ SCARD_PROTOCOL_T0,
+ 0
+};
+
+SCARD_IO_REQUEST g_rgSCardT1Pci = {
+ SCARD_PROTOCOL_T1,
+ 0
+};
+
+SCARD_IO_REQUEST g_rgSCardRawPci = {
+ SCARD_PROTOCOL_RAW,
+ 0
+};
+
+const char *
+pcsc_stringify_error(const LONG err)
+{
+ switch (err) {
+ case SCARD_S_SUCCESS:
+ return ("no error");
+ case SCARD_F_INTERNAL_ERROR:
+ return ("internal error");
+ case SCARD_E_CANCELLED:
+ return ("request cancelled");
+ case SCARD_E_INVALID_HANDLE:
+ return ("invalid handle");
+ case SCARD_E_INVALID_PARAMETER:
+ return ("invalid parameter");
+ case SCARD_E_NO_MEMORY:
+ return ("no memory");
+ case SCARD_E_INSUFFICIENT_BUFFER:
+ return ("buffer was insufficiently sized");
+ case SCARD_E_INVALID_VALUE:
+ return ("invalid value passed");
+ case SCARD_E_UNKNOWN_READER:
+ return ("unknown reader");
+ case SCARD_E_TIMEOUT:
+ return ("timeout occurred");
+ case SCARD_E_SHARING_VIOLATION:
+ return ("sharing violation");
+ case SCARD_E_NO_SMARTCARD:
+ return ("no smartcard present");
+ case SCARD_E_UNKNOWN_CARD:
+ return ("unknown ICC");
+ case SCARD_E_PROTO_MISMATCH:
+ return ("protocol mismatch");
+ case SCARD_F_COMM_ERROR:
+ return ("communication error");
+ case SCARD_F_UNKNOWN_ERROR:
+ return ("unknown error");
+ case SCARD_E_READER_UNAVAILABLE:
+ return ("reader unavailable");
+ case SCARD_E_NO_SERVICE:
+ return ("service error");
+ case SCARD_E_UNSUPPORTED_FEATURE:
+ return ("ICC requires unsupported feature");
+ case SCARD_E_NO_READERS_AVAILABLE:
+ return ("no readers avaiable");
+ case SCARD_W_UNSUPPORTED_CARD:
+ return ("ICC unsupported");
+ case SCARD_W_UNPOWERED_CARD:
+ return ("ICC is not powered");
+ case SCARD_W_RESET_CARD:
+ return ("ICC was reset");
+ case SCARD_W_REMOVED_CARD:
+ return ("ICC has been removed");
+ default:
+ return ("unknown error");
+ }
+}
+
+
+/*
+ * This is called when a caller wishes to open a new Library context.
+ */
+LONG
+SCardEstablishContext(DWORD scope, LPCVOID unused0, LPCVOID unused1,
+ LPSCARDCONTEXT outp)
+{
+ pcsc_hdl_t *hdl;
+
+ if (outp == NULL) {
+ return (SCARD_E_INVALID_PARAMETER);
+ }
+
+ if (scope != SCARD_SCOPE_SYSTEM) {
+ return (SCARD_E_INVALID_VALUE);
+ }
+
+ hdl = calloc(1, sizeof (pcsc_hdl_t));
+ if (hdl == NULL) {
+ return (SCARD_E_NO_MEMORY);
+ }
+
+ hdl->pcsc_create_time = gethrtime();
+ *outp = hdl;
+ return (SCARD_S_SUCCESS);
+}
+
+/*
+ * This is called to free a library context from a client.
+ */
+LONG
+SCardReleaseContext(SCARDCONTEXT hdl)
+{
+ free(hdl);
+ return (SCARD_S_SUCCESS);
+}
+
+/*
+ * This is called to release memory allocated by the library. No, it doesn't
+ * make sense to take a const pointer when being given memory to free. It just
+ * means we have to cast it, but remember: this isn't our API.
+ */
+LONG
+SCardFreeMemory(SCARDCONTEXT unused, LPCVOID mem)
+{
+ free((void *)mem);
+ return (SCARD_S_SUCCESS);
+}
+
+/*
+ * This is called by a caller to get a list of readers that exist in the system.
+ * If lenp is set to SCARD_AUTOALLOCATE, then we are responsible for dealing
+ * with this memory.
+ */
+LONG
+SCardListReaders(SCARDCONTEXT unused, LPCSTR groups, LPSTR bufp, LPDWORD lenp)
+{
+ FTS *fts;
+ FTSENT *ent;
+ char *const root[] = { "/dev/ccid", NULL };
+ char *ubuf;
+ char **readers;
+ uint32_t len, ulen, npaths, nalloc, off, i;
+ int ret;
+
+ if (groups != NULL || lenp == NULL) {
+ return (SCARD_E_INVALID_PARAMETER);
+ }
+
+ fts = fts_open(root, FTS_LOGICAL | FTS_NOCHDIR, NULL);
+ if (fts == NULL) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ return (SCARD_E_NO_READERS_AVAILABLE);
+ case ENOMEM:
+ case EAGAIN:
+ return (SCARD_E_NO_MEMORY);
+ default:
+ return (SCARD_E_NO_SERVICE);
+ }
+ }
+
+ npaths = nalloc = 0;
+ /*
+ * Account for the NUL we'll have to place at the end of this.
+ */
+ len = 1;
+ readers = NULL;
+ while ((ent = fts_read(fts)) != NULL) {
+ size_t plen;
+
+ if (ent->fts_level != 2 || ent->fts_info == FTS_DP)
+ continue;
+
+ if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_NS)
+ continue;
+
+ if (S_ISCHR(ent->fts_statp->st_mode) == 0)
+ continue;
+
+ plen = strlen(ent->fts_path) + 1;
+ if (UINT32_MAX - len <= plen) {
+ /*
+ * I mean, it's true. But I wish I could just give you
+ * EOVERFLOW.
+ */
+ ret = SCARD_E_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (npaths == nalloc) {
+ char **tmp;
+
+ nalloc += 8;
+ tmp = reallocarray(readers, nalloc, sizeof (char *));
+ if (tmp == NULL) {
+ ret = SCARD_E_NO_MEMORY;
+ goto out;
+ }
+ readers = tmp;
+ }
+ readers[npaths] = strdup(ent->fts_path);
+ npaths++;
+ len += plen;
+ }
+
+ if (npaths == 0) {
+ ret = SCARD_E_NO_READERS_AVAILABLE;
+ goto out;
+ }
+
+ ulen = *lenp;
+ *lenp = len;
+ if (ulen != SCARD_AUTOALLOCATE) {
+ if (bufp == NULL) {
+ ret = SCARD_S_SUCCESS;
+ goto out;
+ }
+
+ if (ulen < len) {
+ ret = SCARD_E_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ ubuf = bufp;
+ } else {
+ char **bufpp;
+ if (bufp == NULL) {
+ ret = SCARD_E_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ubuf = malloc(ulen);
+ if (ubuf == NULL) {
+ ret = SCARD_E_NO_MEMORY;
+ goto out;
+ }
+
+ bufpp = (void *)bufp;
+ *bufpp = ubuf;
+ }
+ ret = SCARD_S_SUCCESS;
+
+ for (off = 0, i = 0; i < npaths; i++) {
+ size_t slen = strlen(readers[i]) + 1;
+ bcopy(readers[i], ubuf + off, slen);
+ off += slen;
+ VERIFY3U(off, <=, len);
+ }
+ VERIFY3U(off, ==, len - 1);
+ ubuf[off] = '\0';
+out:
+ for (i = 0; i < npaths; i++) {
+ free(readers[i]);
+ }
+ free(readers);
+ (void) fts_close(fts);
+ return (ret);
+}
+
+static LONG
+uccid_status_helper(int fd, DWORD prots, uccid_cmd_status_t *ucs)
+{
+ /*
+ * Get the status of this slot and find out information about the slot.
+ * We need to see if there's an ICC present and if it matches the
+ * current protocol. If not, then we have to fail this.
+ */
+ bzero(ucs, sizeof (uccid_cmd_status_t));
+ ucs->ucs_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_STATUS, ucs) != 0) {
+ return (SCARD_F_UNKNOWN_ERROR);
+ }
+
+ if ((ucs->ucs_status & UCCID_STATUS_F_CARD_PRESENT) == 0) {
+ return (SCARD_W_REMOVED_CARD);
+ }
+
+ if ((ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE) == 0) {
+ return (SCARD_W_UNPOWERED_CARD);
+ }
+
+ if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) == 0) {
+ return (SCARD_W_UNSUPPORTED_CARD);
+ }
+
+ if ((ucs->ucs_prot & prots) == 0) {
+ return (SCARD_E_PROTO_MISMATCH);
+ }
+
+ return (0);
+}
+
+LONG
+SCardConnect(SCARDCONTEXT hdl, LPCSTR reader, DWORD mode, DWORD prots,
+ LPSCARDHANDLE iccp, LPDWORD protp)
+{
+ LONG ret;
+ uccid_cmd_status_t ucs;
+ pcsc_card_t *card;
+
+ if (reader == NULL) {
+ return (SCARD_E_UNKNOWN_READER);
+ }
+
+ if (iccp == NULL || protp == NULL) {
+ return (SCARD_E_INVALID_PARAMETER);
+ }
+
+ if (mode != SCARD_SHARE_SHARED) {
+ return (SCARD_E_INVALID_VALUE);
+ }
+
+ if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
+ SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
+ return (SCARD_E_INVALID_VALUE);
+ }
+
+ if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
+ return (SCARD_E_UNSUPPORTED_FEATURE);
+ }
+
+ if ((card = malloc(sizeof (*card))) == NULL) {
+ return (SCARD_E_NO_MEMORY);
+ }
+
+ if ((card->pcc_fd = open(reader, O_RDWR)) < 0) {
+ free(card);
+ switch (errno) {
+ case ENOENT:
+ return (SCARD_E_UNKNOWN_READER);
+ default:
+ return (SCARD_F_UNKNOWN_ERROR);
+ }
+ }
+
+ if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0)
+ goto cleanup;
+
+ *protp = ucs.ucs_prot;
+ *iccp = card;
+ return (SCARD_S_SUCCESS);
+cleanup:
+ (void) close(card->pcc_fd);
+ free(card);
+ return (ret);
+}
+
+LONG
+SCardDisconnect(SCARDHANDLE arg, DWORD disposition)
+{
+ pcsc_card_t *card = arg;
+
+ if (arg == NULL) {
+ return (SCARD_E_INVALID_HANDLE);
+ }
+
+ if (disposition != SCARD_LEAVE_CARD) {
+ return (SCARD_E_INVALID_VALUE);
+ }
+
+ if (close(card->pcc_fd) != 0) {
+ return (SCARD_F_UNKNOWN_ERROR);
+ }
+
+ free(card);
+ return (SCARD_S_SUCCESS);
+}
+
+LONG
+SCardBeginTransaction(SCARDHANDLE arg)
+{
+ uccid_cmd_txn_begin_t txn;
+ pcsc_card_t *card = arg;
+
+ if (card == NULL) {
+ return (SCARD_E_INVALID_HANDLE);
+ }
+
+ /*
+ * The semantics of pcsc are that this operation does not block, but
+ * instead fails if we cannot grab it immediately.
+ */
+ bzero(&txn, sizeof (uccid_cmd_txn_begin_t));
+ txn.uct_version = UCCID_CURRENT_VERSION;
+ txn.uct_flags = UCCID_TXN_DONT_BLOCK;
+
+ if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txn) != 0) {
+ VERIFY3S(errno, !=, EFAULT);
+ switch (errno) {
+ case ENODEV:
+ return (SCARD_E_READER_UNAVAILABLE);
+ case EEXIST:
+ /*
+ * This is an odd case. It's used to tell us that we
+ * already have it. For now, just treat it as success.
+ */
+ return (SCARD_S_SUCCESS);
+ case EBUSY:
+ return (SCARD_E_SHARING_VIOLATION);
+ /*
+ * EINPROGRESS is a weird case. It means that we were trying to
+ * grab a hold while another instance using the same handle was.
+ * For now, treat it as an unknown error.
+ */
+ case EINPROGRESS:
+ case EINTR:
+ default:
+ return (SCARD_F_UNKNOWN_ERROR);
+ }
+ }
+ return (SCARD_S_SUCCESS);
+}
+
+LONG
+SCardEndTransaction(SCARDHANDLE arg, DWORD state)
+{
+ uccid_cmd_txn_end_t txn;
+ pcsc_card_t *card = arg;
+
+ if (card == NULL) {
+ return (SCARD_E_INVALID_HANDLE);
+ }
+
+ bzero(&txn, sizeof (uccid_cmd_txn_end_t));
+ txn.uct_version = UCCID_CURRENT_VERSION;
+
+ switch (state) {
+ case SCARD_LEAVE_CARD:
+ txn.uct_flags = UCCID_TXN_END_RELEASE;
+ break;
+ case SCARD_RESET_CARD:
+ txn.uct_flags = UCCID_TXN_END_RESET;
+ break;
+ case SCARD_UNPOWER_CARD:
+ case SCARD_EJECT_CARD:
+ default:
+ return (SCARD_E_INVALID_VALUE);
+ }
+
+ if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txn) != 0) {
+ VERIFY3S(errno, !=, EFAULT);
+ switch (errno) {
+ case ENODEV:
+ return (SCARD_E_READER_UNAVAILABLE);
+ case ENXIO:
+ return (SCARD_E_SHARING_VIOLATION);
+ default:
+ return (SCARD_F_UNKNOWN_ERROR);
+ }
+ }
+ return (SCARD_S_SUCCESS);
+}
+
+LONG
+SCardReconnect(SCARDHANDLE arg, DWORD mode, DWORD prots, DWORD init,
+ LPDWORD protp)
+{
+ uccid_cmd_status_t ucs;
+ pcsc_card_t *card = arg;
+ LONG ret;
+
+ if (card == NULL) {
+ return (SCARD_E_INVALID_HANDLE);
+ }
+
+ if (protp == NULL) {
+ return (SCARD_E_INVALID_PARAMETER);
+ }
+
+ if (mode != SCARD_SHARE_SHARED) {
+ return (SCARD_E_INVALID_VALUE);
+ }
+
+ if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
+ SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
+ return (SCARD_E_INVALID_VALUE);
+ }
+
+ if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
+ return (SCARD_E_UNSUPPORTED_FEATURE);
+ }
+
+ if (init != SCARD_LEAVE_CARD) {
+ return (SCARD_E_INVALID_VALUE);
+ }
+
+ if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0)
+ return (ret);
+
+ *protp = ucs.ucs_prot;
+ return (SCARD_S_SUCCESS);
+}
+
+LONG
+SCardTransmit(SCARDHANDLE arg, const SCARD_IO_REQUEST *sendreq,
+ LPCBYTE sendbuf, DWORD sendlen, SCARD_IO_REQUEST *recvreq, LPBYTE recvbuf,
+ LPDWORD recvlenp)
+{
+ int len;
+ ssize_t ret;
+ pcsc_card_t *card = arg;
+
+ if (card == NULL) {
+ return (SCARD_E_INVALID_HANDLE);
+ }
+
+ /*
+ * Ignore sendreq / recvreq.
+ */
+ if (sendbuf == NULL || recvbuf == NULL || recvlenp == NULL) {
+ return (SCARD_E_INVALID_PARAMETER);
+ }
+
+ /*
+ * The CCID write will always consume all data or none.
+ */
+ ret = write(card->pcc_fd, sendbuf, sendlen);
+ if (ret == -1) {
+ switch (errno) {
+ case E2BIG:
+ return (SCARD_E_INVALID_PARAMETER);
+ case ENODEV:
+ return (SCARD_E_READER_UNAVAILABLE);
+ case EACCES:
+ case EBUSY:
+ return (SCARD_E_SHARING_VIOLATION);
+ case ENXIO:
+ return (SCARD_W_REMOVED_CARD);
+ case EFAULT:
+ return (SCARD_E_INVALID_PARAMETER);
+ case ENOMEM:
+ default:
+ return (SCARD_F_UNKNOWN_ERROR);
+ }
+ }
+ ASSERT3S(ret, ==, sendlen);
+
+ /*
+ * Now, we should be able to block in read.
+ */
+ ret = read(card->pcc_fd, recvbuf, *recvlenp);
+ if (ret == -1) {
+ switch (errno) {
+ case EINVAL:
+ case EOVERFLOW:
+ /*
+ * This means that we need to update len with the real
+ * one.
+ */
+ if (ioctl(card->pcc_fd, FIONREAD, &len) != 0) {
+ return (SCARD_F_UNKNOWN_ERROR);
+ }
+ *recvlenp = len;
+ return (SCARD_E_INSUFFICIENT_BUFFER);
+ case ENODEV:
+ return (SCARD_E_READER_UNAVAILABLE);
+ case EACCES:
+ case EBUSY:
+ return (SCARD_E_SHARING_VIOLATION);
+ case EFAULT:
+ return (SCARD_E_INVALID_PARAMETER);
+ case ENODATA:
+ default:
+ return (SCARD_F_UNKNOWN_ERROR);
+ }
+ }
+
+ *recvlenp = ret;
+
+ return (SCARD_S_SUCCESS);
+}
diff --git a/usr/src/lib/libpcsc/common/mapfile-vers b/usr/src/lib/libpcsc/common/mapfile-vers
new file mode 100644
index 0000000000..5a3786670a
--- /dev/null
+++ b/usr/src/lib/libpcsc/common/mapfile-vers
@@ -0,0 +1,50 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate {
+ global:
+ g_rgSCardRawPci;
+ g_rgSCardT0Pci;
+ g_rgSCardT1Pci;
+ SCardEstablishContext;
+ SCardReleaseContext;
+ SCardFreeMemory;
+ SCardListReaders;
+ SCardConnect;
+ SCardDisconnect;
+ SCardBeginTransaction;
+ SCardEndTransaction;
+ SCardReconnect;
+ SCardTransmit;
+ pcsc_stringify_error;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libpcsc/common/winscard.h b/usr/src/lib/libpcsc/common/winscard.h
new file mode 100644
index 0000000000..bec4960040
--- /dev/null
+++ b/usr/src/lib/libpcsc/common/winscard.h
@@ -0,0 +1,144 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#ifndef _WINSCARD_H
+#define _WINSCARD_H
+
+/*
+ * This library provides a compatibility interface with programs designed
+ * against the PC SmartCard Library. This originates from Microsoft and has been
+ * used on a few different forms over the years by folks. The purpose of this
+ * library is for compatibility.
+ *
+ * At the time of this writing, Microsofts API documentation can be found here:
+ * https://docs.microsoft.com/en-us/windows/win32/api/winscard/
+ *
+ * New consumers should not use this library and instead should leverage
+ * ccid(7D) instead.
+ */
+
+#include <stdint.h>
+#include <wintypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This is a departure from the PCSC system which defines this as a LONG,
+ * which is the same size on 32bit and 64bit Windows (ILP32 and LLP64).
+ * We need to use the real native pointer size for the context handle as
+ * it wouldn't fit into a LONG on our LP64 platform.
+ */
+typedef void *SCARDCONTEXT;
+typedef void **PSCARDCONTEXT;
+typedef void **LPSCARDCONTEXT;
+typedef void *SCARDHANDLE;
+typedef void **PSCARDHANDLE;
+typedef void **LPSCARDHANDLE;
+
+/*
+ * Conventionally this is supposed to be packed.
+ */
+#pragma pack(1)
+typedef struct {
+ unsigned long dwProtocol;
+ unsigned long cbPciLength;
+} SCARD_IO_REQUEST, *PSCARD_IO_REQUEST, *LPSCARD_IO_REQUEST;
+#pragma pack()
+
+extern SCARD_IO_REQUEST g_rgSCardT0Pci, g_rgSCardT1Pci, g_rgSCardRawPci;
+#define SCARD_PCI_T0 (&g_rgSCardT0Pci)
+#define SCARD_PCI_T1 (&g_rgSCardT1Pci)
+#define SCARD_PCI_RAW (&g_rgSCardRawPci)
+
+/*
+ * Return values and error codes. We strive to use the same error codes as
+ * Microsoft.
+ */
+#define SCARD_S_SUCCESS ((LONG)0x00000000)
+#define SCARD_F_INTERNAL_ERROR ((LONG)0x80100001)
+#define SCARD_E_CANCELLED ((LONG)0x80100002)
+#define SCARD_E_INVALID_HANDLE ((LONG)0x80100003)
+#define SCARD_E_INVALID_PARAMETER ((LONG)0x80100004)
+#define SCARD_E_NO_MEMORY ((LONG)0x80100006)
+#define SCARD_E_INSUFFICIENT_BUFFER ((LONG)0x80100008)
+#define SCARD_E_UNKNOWN_READER ((LONG)0x80100009)
+#define SCARD_E_TIMEOUT ((LONG)0x8010000a)
+#define SCARD_E_SHARING_VIOLATION ((LONG)0x8010000b)
+#define SCARD_E_NO_SMARTCARD ((LONG)0x8010000c)
+#define SCARD_E_UNKNOWN_CARD ((LONG)0x8010000d)
+#define SCARD_E_PROTO_MISMATCH ((LONG)0x8010000f)
+#define SCARD_E_INVALID_VALUE ((LONG)0x80100011)
+#define SCARD_F_COMM_ERROR ((LONG)0x80100013)
+#define SCARD_F_UNKNOWN_ERROR ((LONG)0x80100014)
+#define SCARD_E_READER_UNAVAILABLE ((LONG)0x80100017)
+#define SCARD_E_NO_SERVICE ((LONG)0x8010001D)
+#define SCARD_E_UNSUPPORTED_FEATURE ((LONG)0x80100022)
+#define SCARD_E_NO_READERS_AVAILABLE ((LONG)0x8010002E)
+#define SCARD_W_UNSUPPORTED_CARD ((LONG)0x80100065)
+#define SCARD_W_UNPOWERED_CARD ((LONG)0x80100067)
+#define SCARD_W_RESET_CARD ((LONG)0x80100068)
+#define SCARD_W_REMOVED_CARD ((LONG)0x80100069)
+
+#define SCARD_SCOPE_USER 0x0000
+#define SCARD_SCOPE_TERMINAL 0x0001
+#define SCARD_SCOPE_SYSTEM 0x0002
+#define SCARD_SCOPE_GLOBAL 0x0003
+
+#define SCARD_SHARE_EXCLUSIVE 0x0001
+#define SCARD_SHARE_SHARED 0x0002
+#define SCARD_SHARE_DIRECT 0x0003
+
+#define SCARD_PROTOCOL_T0 0x0001
+#define SCARD_PROTOCOL_T1 0x0002
+#define SCARD_PROTOCOL_RAW 0x0004
+#define SCARD_PROTOCOL_T15 0x0008
+
+#define SCARD_LEAVE_CARD 0x0000
+#define SCARD_RESET_CARD 0x0001
+#define SCARD_UNPOWER_CARD 0x0002
+#define SCARD_EJECT_CARD 0x0003
+
+/*
+ * This is used to indicate that the framework should allocate memory.
+ */
+#define SCARD_AUTOALLOCATE UINT32_MAX
+
+extern LONG SCardEstablishContext(DWORD, LPCVOID, LPCVOID, LPSCARDCONTEXT);
+extern LONG SCardReleaseContext(SCARDCONTEXT);
+
+extern LONG SCardListReaders(SCARDCONTEXT, LPCSTR, LPSTR, LPDWORD);
+
+extern LONG SCardFreeMemory(SCARDCONTEXT, LPCVOID);
+
+extern LONG SCardConnect(SCARDCONTEXT, LPCSTR, DWORD, DWORD, LPSCARDHANDLE,
+ LPDWORD);
+extern LONG SCardDisconnect(SCARDHANDLE, DWORD);
+
+extern LONG SCardBeginTransaction(SCARDHANDLE);
+extern LONG SCardEndTransaction(SCARDHANDLE, DWORD);
+extern LONG SCardReconnect(SCARDHANDLE, DWORD, DWORD, DWORD, LPDWORD);
+
+extern LONG SCardTransmit(SCARDHANDLE, const SCARD_IO_REQUEST *, LPCBYTE,
+ DWORD, SCARD_IO_REQUEST *, LPBYTE, LPDWORD);
+
+extern const char *pcsc_stringify_error(const LONG);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINSCARD_H */
diff --git a/usr/src/lib/libpcsc/common/wintypes.h b/usr/src/lib/libpcsc/common/wintypes.h
new file mode 100644
index 0000000000..9bb50a87cc
--- /dev/null
+++ b/usr/src/lib/libpcsc/common/wintypes.h
@@ -0,0 +1,50 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+#ifndef _WINTYPES_H
+#define _WINTYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * While we don't want to, this expects that we have Win32 style type names.
+ * Deal with conversions between Win32 and reality. Remember that Windows is an
+ * ILP32 system, but it is a LLP64 system.
+ */
+
+typedef uint8_t BYTE;
+typedef uint8_t *LPBYTE;
+typedef const uint8_t *LPCBYTE;
+typedef const void *LPCVOID;
+typedef uint32_t DWORD;
+typedef uint32_t *LPDWORD;
+typedef int32_t LONG;
+typedef char *LPSTR;
+typedef const char *LPCSTR;
+
+/*
+ * Include a few deprecated types because folks still use them.
+ */
+typedef char *LPTSTR;
+typedef const char *LPCTSTR;
+typedef char *LPCWSTR;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINTYPES_H */
diff --git a/usr/src/lib/libpcsc/i386/Makefile b/usr/src/lib/libpcsc/i386/Makefile
new file mode 100644
index 0000000000..e4bd71f624
--- /dev/null
+++ b/usr/src/lib/libpcsc/i386/Makefile
@@ -0,0 +1,18 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/libsasl/lib/common.c b/usr/src/lib/libsasl/lib/common.c
index b4fe5d1d86..e91a5b5508 100644
--- a/usr/src/lib/libsasl/lib/common.c
+++ b/usr/src/lib/libsasl/lib/common.c
@@ -1301,7 +1301,7 @@ static reg_list_t *reg_list_base = NULL;
int _is_sun_reg(void *mech)
{
- reg_list_t *r, *prev;
+ reg_list_t *r, *prev = NULL;
int is_reg = 0;
LOCK_MUTEX(&reg_mutex);
diff --git a/usr/src/lib/libshell/amd64/src/cmd/ksh93/FEATURE/math b/usr/src/lib/libshell/amd64/src/cmd/ksh93/FEATURE/math
index 91c42367be..c6ec7ce3d6 100644
--- a/usr/src/lib/libshell/amd64/src/cmd/ksh93/FEATURE/math
+++ b/usr/src/lib/libshell/amd64/src/cmd/ksh93/FEATURE/math
@@ -130,28 +130,28 @@ const struct mathtab shtab_math[] =
"\002fmax", (Math_f)fmaxl,
"\002fmin", (Math_f)fminl,
"\002fmod", (Math_f)fmodl,
- "\011fpclassify", (Math_f)local_fpclassify,
+ "\011fpclassify", (Math_f)(uintptr_t)local_fpclassify,
"\002hypot", (Math_f)hypotl,
- "\011ilogb", (Math_f)ilogbl,
- "\011isfinite", (Math_f)local_isfinite,
- "\012isgreater", (Math_f)local_isgreater,
- "\012isgreaterequal", (Math_f)local_isgreaterequal,
- "\011isinf", (Math_f)local_isinf,
- "\012isless", (Math_f)local_isless,
- "\012islessequal", (Math_f)local_islessequal,
- "\012islessgreater", (Math_f)local_islessgreater,
- "\011isnan", (Math_f)isnanl,
- "\011isnormal", (Math_f)local_isnormal,
+ "\011ilogb", (Math_f)(uintptr_t)ilogbl,
+ "\011isfinite", (Math_f)(uintptr_t)local_isfinite,
+ "\012isgreater", (Math_f)(uintptr_t)local_isgreater,
+ "\012isgreaterequal", (Math_f)(uintptr_t)local_isgreaterequal,
+ "\011isinf", (Math_f)(uintptr_t)local_isinf,
+ "\012isless", (Math_f)(uintptr_t)local_isless,
+ "\012islessequal", (Math_f)(uintptr_t)local_islessequal,
+ "\012islessgreater", (Math_f)(uintptr_t)local_islessgreater,
+ "\011isnan", (Math_f)(uintptr_t)isnanl,
+ "\011isnormal", (Math_f)(uintptr_t)local_isnormal,
#ifdef FP_SUBNORMAL
- "\011issubnormal", (Math_f)local_issubnormal,
+ "\011issubnormal", (Math_f)(uintptr_t)local_issubnormal,
#endif
- "\012isunordered", (Math_f)local_isunordered,
+ "\012isunordered", (Math_f)(uintptr_t)local_isunordered,
#ifdef FP_ZERO
- "\011iszero", (Math_f)local_iszero,
+ "\011iszero", (Math_f)(uintptr_t)local_iszero,
#endif
"\001j0", (Math_f)j0l,
"\001j1", (Math_f)j1l,
- "\002jn", (Math_f)jnl,
+ "\002jn", (Math_f)(uintptr_t)jnl,
"\001lgamma", (Math_f)lgammal,
"\001log", (Math_f)logl,
"\001log10", (Math_f)log10l,
@@ -167,7 +167,7 @@ const struct mathtab shtab_math[] =
"\001round", (Math_f)roundl,
"\002scalb", (Math_f)scalbl,
"\002scalbn", (Math_f)scalbnl,
- "\011signbit", (Math_f)local_signbit,
+ "\011signbit", (Math_f)(uintptr_t)local_signbit,
"\001sin", (Math_f)sinl,
"\001sinh", (Math_f)sinhl,
"\001sqrt", (Math_f)sqrtl,
@@ -177,7 +177,7 @@ const struct mathtab shtab_math[] =
"\001trunc", (Math_f)truncl,
"\001y0", (Math_f)y0l,
"\001y1", (Math_f)y1l,
- "\002yn", (Math_f)ynl,
+ "\002yn", (Math_f)(uintptr_t)ynl,
"", (Math_f)0
};
#endif
diff --git a/usr/src/lib/libshell/common/sh/streval.c b/usr/src/lib/libshell/common/sh/streval.c
index 2b7a677f68..11df8402e8 100644
--- a/usr/src/lib/libshell/common/sh/streval.c
+++ b/usr/src/lib/libshell/common/sh/streval.c
@@ -391,7 +391,7 @@ Sfdouble_t arith_exec(Arith_t *ep)
sp--,tp--;
fun = *((Math_f*)(ep->code+(int)(*sp)));
type = *tp;
- num = (*((Math_1i_f)fun))(num);
+ num = (*((Math_1i_f)(uintptr_t)fun))(num);
break;
case A_CALL2F:
sp-=2,tp-=2;
@@ -403,7 +403,7 @@ Sfdouble_t arith_exec(Arith_t *ep)
sp-=2,tp-=2;
fun = *((Math_f*)(ep->code+(int)(*sp)));
type = *tp;
- num = (*((Math_2i_f)fun))(sp[1],num);
+ num = (*((Math_2i_f)(uintptr_t)fun))(sp[1],num);
break;
case A_CALL3F:
sp-=3,tp-=3;
diff --git a/usr/src/lib/libshell/i386/src/cmd/ksh93/FEATURE/math b/usr/src/lib/libshell/i386/src/cmd/ksh93/FEATURE/math
index fbe05f198a..d425e09207 100644
--- a/usr/src/lib/libshell/i386/src/cmd/ksh93/FEATURE/math
+++ b/usr/src/lib/libshell/i386/src/cmd/ksh93/FEATURE/math
@@ -130,28 +130,28 @@ const struct mathtab shtab_math[] =
"\002fmax", (Math_f)fmaxl,
"\002fmin", (Math_f)fminl,
"\002fmod", (Math_f)fmodl,
- "\011fpclassify", (Math_f)local_fpclassify,
+ "\011fpclassify", (Math_f)(uintptr_t)local_fpclassify,
"\002hypot", (Math_f)hypotl,
- "\011ilogb", (Math_f)ilogbl,
- "\011isfinite", (Math_f)local_isfinite,
- "\012isgreater", (Math_f)local_isgreater,
- "\012isgreaterequal", (Math_f)local_isgreaterequal,
- "\011isinf", (Math_f)local_isinf,
- "\012isless", (Math_f)local_isless,
- "\012islessequal", (Math_f)local_islessequal,
- "\012islessgreater", (Math_f)local_islessgreater,
- "\011isnan", (Math_f)isnanl,
- "\011isnormal", (Math_f)local_isnormal,
+ "\011ilogb", (Math_f)(uintptr_t)ilogbl,
+ "\011isfinite", (Math_f)(uintptr_t)local_isfinite,
+ "\012isgreater", (Math_f)(uintptr_t)local_isgreater,
+ "\012isgreaterequal", (Math_f)(uintptr_t)local_isgreaterequal,
+ "\011isinf", (Math_f)(uintptr_t)local_isinf,
+ "\012isless", (Math_f)(uintptr_t)local_isless,
+ "\012islessequal", (Math_f)(uintptr_t)local_islessequal,
+ "\012islessgreater", (Math_f)(uintptr_t)local_islessgreater,
+ "\011isnan", (Math_f)(uintptr_t)isnanl,
+ "\011isnormal", (Math_f)(uintptr_t)local_isnormal,
#ifdef FP_SUBNORMAL
- "\011issubnormal", (Math_f)local_issubnormal,
+ "\011issubnormal", (Math_f)(uintptr_t)local_issubnormal,
#endif
- "\012isunordered", (Math_f)local_isunordered,
+ "\012isunordered", (Math_f)(uintptr_t)local_isunordered,
#ifdef FP_ZERO
- "\011iszero", (Math_f)local_iszero,
+ "\011iszero", (Math_f)(uintptr_t)local_iszero,
#endif
"\001j0", (Math_f)j0l,
"\001j1", (Math_f)j1l,
- "\002jn", (Math_f)jnl,
+ "\002jn", (Math_f)(uintptr_t)jnl,
"\001lgamma", (Math_f)lgammal,
"\001log", (Math_f)logl,
"\001log10", (Math_f)log10l,
@@ -167,7 +167,7 @@ const struct mathtab shtab_math[] =
"\001round", (Math_f)roundl,
"\002scalb", (Math_f)scalbl,
"\002scalbn", (Math_f)scalbnl,
- "\011signbit", (Math_f)local_signbit,
+ "\011signbit", (Math_f)(uintptr_t)local_signbit,
"\001sin", (Math_f)sinl,
"\001sinh", (Math_f)sinhl,
"\001sqrt", (Math_f)sqrtl,
@@ -177,7 +177,7 @@ const struct mathtab shtab_math[] =
"\001trunc", (Math_f)truncl,
"\001y0", (Math_f)y0l,
"\001y1", (Math_f)y1l,
- "\002yn", (Math_f)ynl,
+ "\002yn", (Math_f)(uintptr_t)ynl,
"", (Math_f)0
};
#endif
diff --git a/usr/src/lib/libsmbios/common/mapfile-vers b/usr/src/lib/libsmbios/common/mapfile-vers
index e1e063ea4b..dd09785fe3 100644
--- a/usr/src/lib/libsmbios/common/mapfile-vers
+++ b/usr/src/lib/libsmbios/common/mapfile-vers
@@ -101,10 +101,13 @@ SYMBOL_VERSION SUNWprivate_1.1 {
smbios_info_memdevmap;
smbios_info_obdevs;
smbios_info_obdevs_ext;
+ smbios_info_pointdev;
smbios_info_port;
smbios_info_extport;
smbios_info_powersup;
smbios_info_processor;
+ smbios_info_processor_info;
+ smbios_info_processor_riscv;
smbios_info_extprocessor;
smbios_info_slot;
smbios_info_slot_peers;
@@ -137,6 +140,8 @@ SYMBOL_VERSION SUNWprivate_1.1 {
smbios_memdevice_rank_desc;
smbios_open;
smbios_onboard_type_desc;
+ smbios_pointdev_iface_desc;
+ smbios_pointdev_type_desc;
smbios_port_conn_desc;
smbios_port_type_desc;
smbios_powersup_flag_desc;
@@ -150,7 +155,13 @@ SYMBOL_VERSION SUNWprivate_1.1 {
smbios_processor_upgrade_desc;
smbios_processor_core_flag_desc;
smbios_processor_core_flag_name;
+ smbios_processor_info_type_desc;
smbios_psn;
+ smbios_riscv_isa_desc;
+ smbios_riscv_isa_name;
+ smbios_riscv_priv_desc;
+ smbios_riscv_priv_name;
+ smbios_riscv_width_desc;
smbios_slot_ch1_desc;
smbios_slot_ch1_name;
smbios_slot_ch2_desc;
diff --git a/usr/src/lib/smbclnt/libfksmbfs/common/fake_vfs.c b/usr/src/lib/smbclnt/libfksmbfs/common/fake_vfs.c
index cc63b01ee1..c17602ff39 100644
--- a/usr/src/lib/smbclnt/libfksmbfs/common/fake_vfs.c
+++ b/usr/src/lib/smbclnt/libfksmbfs/common/fake_vfs.c
@@ -284,8 +284,9 @@ fs_copyfsops(const fs_operation_def_t *template, vfsops_t *actual,
fs_nosys, fs_nosys,
VFSNAME_FREEVFS, offsetof(vfsops_t, vfs_freevfs),
- (fs_generic_func_p)fs_freevfs,
- (fs_generic_func_p)fs_freevfs, /* Shouldn't fail */
+ (fs_generic_func_p)(uintptr_t)fs_freevfs,
+ (fs_generic_func_p)(uintptr_t)
+ fs_freevfs, /* Shouldn't fail */
VFSNAME_VNSTATE, offsetof(vfsops_t, vfs_vnstate),
(fs_generic_func_p)fs_nosys,
diff --git a/usr/src/lib/smbclnt/libfksmbfs/common/fake_vnode.c b/usr/src/lib/smbclnt/libfksmbfs/common/fake_vnode.c
index 07eb5bc18d..6fef7649ae 100644
--- a/usr/src/lib/smbclnt/libfksmbfs/common/fake_vnode.c
+++ b/usr/src/lib/smbclnt/libfksmbfs/common/fake_vnode.c
@@ -192,8 +192,8 @@ static const fs_operation_trans_def_t vn_ops_table[] = {
fs_rwlock, fs_rwlock,
VOPNAME_RWUNLOCK, offsetof(struct vnodeops, vop_rwunlock),
- (fs_generic_func_p) fs_rwunlock,
- (fs_generic_func_p) fs_rwunlock, /* no errors allowed */
+ (fs_generic_func_p)(uintptr_t)fs_rwunlock,
+ (fs_generic_func_p)(intptr_t)fs_rwunlock, /* no errors allowed */
VOPNAME_SEEK, offsetof(struct vnodeops, vop_seek),
fs_nosys, fs_nosys,
@@ -243,8 +243,8 @@ static const fs_operation_trans_def_t vn_ops_table[] = {
fs_nosys, fs_nosys,
VOPNAME_DISPOSE, offsetof(struct vnodeops, vop_dispose),
- (fs_generic_func_p) fs_dispose,
- (fs_generic_func_p) fs_nodispose,
+ (fs_generic_func_p)(intptr_t)fs_dispose,
+ (fs_generic_func_p)(intptr_t)fs_nodispose,
VOPNAME_SETSECATTR, offsetof(struct vnodeops, vop_setsecattr),
fs_nosys, fs_nosys,
diff --git a/usr/src/man/man1/sleep.1 b/usr/src/man/man1/sleep.1
index 4b90194707..3ee7898bfa 100644
--- a/usr/src/man/man1/sleep.1
+++ b/usr/src/man/man1/sleep.1
@@ -44,202 +44,133 @@
.\" Portions Copyright (c) 1992, X/Open Company Limited All Rights Reserved
.\" Portions Copyright (c) 1982-2007 AT&T Knowledge Ventures
.\" Copyright (c) 2007, Sun Microsystems, Inc. All Rights Reserved Portions
+.\" Copyright 2019 Robert Mustacchi
.\"
-.TH SLEEP 1 "May 13, 2017"
-.SH NAME
-sleep \- suspend execution for an interval
-.SH SYNOPSIS
-.SS "/usr/bin/sleep"
-.LP
-.nf
-\fB/usr/bin/sleep\fR \fIseconds\fR
-.fi
-
-.SS "ksh93"
-.LP
-.nf
-\fBsleep\fR \fIseconds\fR
-.fi
-
-.SH DESCRIPTION
-.LP
-\fBsleep\fR suspends execution for at least the time in seconds specified by
-\fIseconds\fR or until a \fBSIGALRM\fR signal is received. The \fIseconds\fR
-operand can be specified as a floating point number but the actual granularity
-normally depends on the underlying system.
-.SH OPERANDS
-.SS "/usr/bin/sleep"
-.LP
-The following operands are supported for \fB/usr/bin/sleep\fR and \fBksh93\fR's
-\fBsleep\fR built-in command:
-.sp
-.ne 2
-.na
-\fB\fIseconds\fR\fR
-.ad
-.RS 11n
-A non-negative floating-point number specifying the number of seconds for which
-to suspend execution. The floating-point number may be specified in all formats
-required by C99/XPG6, including constants such as "Inf" or "infinity".
-.RE
-
-.SS "ksh93"
-.LP
-The following operands are supported:
-.sp
-.ne 2
-.na
-\fB\fItime\fR\fR
-.ad
-.RS 8n
-Specify \fItime\fR in seconds as a floating point number. The actual
-granularity depends on the underlying system, normally around 1 millisecond.
-.RE
-
-.SH EXAMPLES
-.LP
-\fBExample 1 \fRSuspending Command Execution
-.sp
-.LP
+.Dd September 12, 2019
+.Dt SLEEP 1
+.Os
+.Sh NAME
+.Nm sleep
+.Nd suspend execution for an interval
+.Sh SYNOPSIS
+.Nm sleep
+.Ar time[suffix]
+.Sh DESCRIPTION
+.Nm
+suspends execution for at least the time in seconds specified by
+.Ar time
+or until a
+.Dv SIGALRM
+signal is received.
+The
+.Ar time
+operand can be specified as a non-negative floating point number but the
+actual granularity depends on the underlying system.
+The
+.Ar time
+operand may be passed as a decimal or hexadecimal string.
+Other floating point values such as Inf or infinity are also honored.
+.Pp
+A single suffix may be applied to the
+.Ar time
+operand to represent units other than seconds.
+Supported suffixes for the
+.Ar suffix
+operand include:
+.Bl -tag -offset indent -width 6n
+.It Sy s
+Represents time in seconds.
+.It Sy m
+Represents time in minutes.
+.It Sy h
+Represents time in hours.
+.It Sy d
+Represents time in days.
+.It Sy w
+Represents time in weeks.
+.It Sy y
+Represents time in years.
+.El
+.Pp
+The use of suffixes is generally not portable to other systems.
+.Ss Signals
+If the
+.Nm
+program receives a signal, unless it is the
+.Dv SIGALRM
+signal, it will follow with the default signal handling disposition.
+If such a signal would interrupt the sleep, then the program may
+terminate with an error.
+.Sh EXIT STATUS
+The following exit values are returned:
+.Bl -inset
+.It Sy 0
+The execution was successfully suspended for at least
+.Ar time
+seconds, or a
+.Dv SIGALRM
+signal was received.
+.It Sy >0
+An error has occurred.
+.El
+.Sh EXAMPLES
+.Sy Example 1
+Suspending Command Execution
+.Pp
The following example executes a command after a certain amount of time:
-
-.sp
-.in +2
-.nf
+.Bd -literal -offset indent
example% \fB(sleep 105; \fIcommand\fR)&\fR
-.fi
-.in -2
-.sp
-
-.LP
-\fBExample 2 \fRExecuting a Command Every So Often
-.sp
-.LP
+.Ed
+.Pp
+.Sy Example 2
+Executing a Command Every So Often
+.Pp
The following example executes a command every so often:
-
-.sp
-.in +2
-.nf
-example% \fBwhile true
-do
+.Bd -literal -offset indent
+example% \fBwhile true; do
\fIcommand\fR
sleep 37
done\fR
-.fi
-.in -2
-.sp
-
-.LP
-\fBExample 3 \fRSuspend command execution forever (or until a \fBSIGALRM\fR
-signal is received)
-.sp
-.in +2
-.nf
+.Ed
+.Pp
+.Sy Example 3
+Suspend command execution forever
+.Po
+or until a
+.Dv SIGALRM
+signal is received
+.Pc
+.Bd -literal
example% sleep Inf
-.fi
-.in -2
-.sp
-
-.LP
-\fBExample 4 \fRSuspending command execution for 0.5 seconds
-.sp
-.LP
+.Ed
+.Pp
+.Sy Example 4
+Suspending command execution for 0.5 seconds
+.Pp
Suspending command execution for 0.5 seconds using an alternative
-floating-point representation for the value "0.5"
-
-.sp
-.in +2
-.nf
+floating-point representation for the value "0.5".
+.Bd -literal
example% printf "%a\en" 0.5
0x1.0000000000000000000000000000p-01
-.fi
-.in -2
-.sp
-
-.sp
-.in +2
-.nf
example% sleep 0x1.0000000000000000000000000000p-01
-.fi
-.in -2
-.sp
-
-.SH ENVIRONMENT VARIABLES
-.LP
-See \fBenviron\fR(5) for descriptions of the following environment variables
-that affect the execution of \fBsleep\fR: \fBLANG\fR, \fBLC_ALL\fR,
-\fBLC_CTYPE\fR, \fBLC_MESSAGES\fR, and \fBNLSPATH\fR.
-.SH EXIT STATUS
-.LP
-The following exit values are returned:
-.sp
-.ne 2
-.na
-\fB\fB0\fR\fR
-.ad
-.RS 6n
-The execution was successfully suspended for at least \fItime\fR seconds, or a
-\fBSIGALRM\fR signal was received (see NOTES).
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fB>0\fR\fR
-.ad
-.RS 6n
-An error has occurred.
-.RE
-
-.SH ATTRIBUTES
-.LP
-See \fBattributes\fR(5) for descriptions of the following attributes:
-.SS "/usr/bin/sleep"
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE ATTRIBUTE VALUE
-_
-Interface Stability Committed
-_
-Standard See \fBstandards\fR(5).
-.TE
-
-.SS "ksh93"
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE ATTRIBUTE VALUE
-_
-Interface Stability Uncommitted
-.TE
-
-.SH SEE ALSO
-.LP
-\fBksh93\fR(1), \fBwait\fR(1), \fBalarm\fR(2), \fBsleep\fR(3C),
-\fBattributes\fR(5), \fBenviron\fR(5), \fBstandards\fR(5)
-.SH NOTES
-.LP
-If the \fBsleep\fR utility receives a \fBSIGALRM\fR signal, one of the
-following actions is taken:
-.RS +4
-.TP
-.ie t \(bu
-.el o
-Terminate normally with a zero exit status.
-.RE
-.RS +4
-.TP
-.ie t \(bu
-.el o
-Effectively ignore the signal.
-.RE
-.sp
-.LP
-The \fBsleep\fR utility takes the standard action for all other signals.
-.sp
-.LP
-The behavior for input values such as "NaN" (not-a-number) or negative values
-is undefined.
+.Ed
+.Sh ENVIRONMENT VARIABLES
+See
+.Xr environ 5
+for descriptions of the following environment variables
+that affect the execution of
+.Nm :
+.Ev LANG ,
+.Ev LC_ALL ,
+.Ev LC_MESSAGES ,
+.Ev LC_NUMERIC ,
+and
+.Ev NLSPATH .
+.Sh INTERFACE STABILITY
+.Sy Committed
+.Sh SEE ALSO
+.Xr wait 1 ,
+.Xr alarm 2 ,
+.Xr nanosleep 3C ,
+.Xr sleep 3C ,
+.Xr environ 5
diff --git a/usr/src/man/man1m/Makefile b/usr/src/man/man1m/Makefile
index 2152864b8d..f5683c36fd 100644
--- a/usr/src/man/man1m/Makefile
+++ b/usr/src/man/man1m/Makefile
@@ -60,6 +60,7 @@ _MANFILES= 6to4relay.1m \
busstat.1m \
captoinfo.1m \
catman.1m \
+ ccidadm.1m \
cfgadm.1m \
cfgadm_ac.1m \
cfgadm_cardbus.1m \
diff --git a/usr/src/man/man1m/ccidadm.1m b/usr/src/man/man1m/ccidadm.1m
new file mode 100644
index 0000000000..686db8cb46
--- /dev/null
+++ b/usr/src/man/man1m/ccidadm.1m
@@ -0,0 +1,120 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2019 Joyent, Inc.
+.\"
+.Dd December 17, 2019
+.Dt CCIDADM 1M
+.Os
+.Sh NAME
+.Nm ccidadm
+.Nd CCID administration utility
+.Sh SYNOPSIS
+.Nm /usr/lib/ccid/ccidadm
+.Ar list
+.Nm /usr/lib/ccid/ccidadm
+.Ar atr
+.Op Fl pvx
+.Op Ar device
+.Nm /usr/lib/ccid/ccidadm
+.Ar reader
+.Op Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to list the CCID controllers and their slots known to the
+.Xr ccid 7D
+driver, query the features and capabilities of a CCID controller, and print
+the ATR of an ICC (integrated circuit card) that is inserted in a slot on an
+CCID controller.
+.Pp
+The information returned by the hardware is printed by
+.Nm
+in a human-readable form were applicable.
+.Sh ARGUMENTS
+.Nm
+expects the following kinds of arguments:
+.Bl -tag -width "device"
+.It Ar command
+Any command
+.Nm
+understands.
+See section
+.Sx COMMANDS .
+.It Ar device
+Specifies a CCID reader or a slot, either as absolute path to the device node
+or in a short-hand form.
+The short-hand form consists of the reader instance, specified by the driver
+name
+.Qq ccid
+followed by the instance number of the reader, and optionally a slot instance
+separated by a
+.Qq / ,
+consisting of the word
+.Qq slot
+followed by the slot number.
+Here's an example for slot 1 on ccid reader 5:
+.Qq ccid5/slot1
+.El
+.Sh COMMANDS
+.Bl -tag -width ""
+.It Xo
+.Nm
+.Cm list
+.Xc
+Lists the CCID controllers and their slots known to the system and prints their
+product name, device node, card state, and the transport protocol in use.
+.It Xo
+.Nm
+.Cm atr
+.Op Fl pvx
+.Op Ar device
+.Xc
+Prints the ATR of an ICC that is inserted in the specified slot.
+If a device is specified it must refer to a certain slot.
+If no device is specified the command will print the ATR of all inserted slots
+in the system.
+A human-readable summary of the ATR data is printed when no flags are given.
+The following options can be used to alter the output of the
+.Cm atr
+command::
+.Bl -tag -width Ds
+.It Fl v
+Verbose output, the individual bytes of the ATR are printed and decoded
+bit-by-bit in a human-readable form.
+Additionally the historic data in the ATR is printed as a hexadecimal dump.
+.It Fl x
+The complete ATR is printed as a hexadecimal dump.
+.El
+.It Xo
+.Nm
+.Cm reader
+.Op Ar device
+.Xc
+Print the capabilities of the specified CCID reader.
+Specifying slot number is not required but can optionally be specified.
+If no device is given, the command will print the capabilities of all attached
+CCID readers.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh INTERFACE STABILITY
+The command line interface of
+.Nm
+is
+.Sy Evolving .
+The output of
+.Nm
+is
+.Sy Not-an-Interface
+and may change any time.
+.Sh SEE ALSO
+.Xr ccid 7D
diff --git a/usr/src/man/man7d/Makefile b/usr/src/man/man7d/Makefile
index c652a3134f..327c7b73b9 100644
--- a/usr/src/man/man7d/Makefile
+++ b/usr/src/man/man7d/Makefile
@@ -36,6 +36,7 @@ _MANFILES= aac.7d \
blkdev.7d \
bnxe.7d \
bscv.7d \
+ ccid.7d \
chxge.7d \
console.7d \
cpuid.7d \
diff --git a/usr/src/man/man7d/audio.7d b/usr/src/man/man7d/audio.7d
index 9a50a93d0d..76aa2bdf8e 100644
--- a/usr/src/man/man7d/audio.7d
+++ b/usr/src/man/man7d/audio.7d
@@ -3,12 +3,10 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the
.\" fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH AUDIO 7D "Aug 3, 2009"
+.TH AUDIO 7D "Jan 10, 2020"
.SH NAME
audio \- common audio framework
.SH DESCRIPTION
-.sp
-.LP
The \fBaudio\fR driver provides common support routines for audio devices in
Solaris.
.sp
@@ -20,25 +18,17 @@ to be accessed with different programming interfaces.
The audio framework also provides a number of facilities, such as mixing of
audio streams, and data format and sample rate conversion.
.SS "Overview"
-.sp
-.LP
The audio framework provides a software mixing engine (audio mixer) for all
audio devices, allowing more than one process to play or record audio at the
same time.
.SS "Multi-Stream Codecs"
-.sp
-.LP
The audio mixer supports multi-stream Codecs. These devices have DSP engines
that provide sample rate conversion, hardware mixing, and other features. The
use of such hardware features is opaque to applications.
.SS "Backward Compatibility"
-.sp
-.LP
It is not possible to disable the mixing function. Applications must not assume
that they have exclusive access to the audio device.
.SS "Audio Formats"
-.sp
-.LP
Digital audio data represents a quantized approximation of an analog audio
signal waveform. In the simplest case, these quantized numbers represent the
amplitude of the input waveform at particular sampling intervals. To achieve
@@ -58,8 +48,6 @@ addition to the formats that the audio device supports directly, other formats
provide higher data compression. Applications can convert audio data to and
from these formats when playing or recording.
.SS "Sample Rate"
-.sp
-.LP
Sample rate is a number that represents the sampling frequency (in samples per
second) of the audio data.
.sp
@@ -70,7 +58,7 @@ require compute-intensive low pass filtering. The result is that high sample
rate audio streams are not degraded by filtering.
.sp
.LP
-Sample rate conversion can be a compute-intensive operation, dependingon the
+Sample rate conversion can be a compute-intensive operation, depending on the
number of channels and a device's sample rate. For example, an 8KHz signal can
be easily converted to 48KHz, requiring a low cost up sampling by 6. However,
converting from 44.1KHz to 48KHz is computer intensive because it must be up
@@ -89,8 +77,6 @@ All modern audio devices run at 48 kHz or a multiple thereof, hence just using
48 kHz can be a reasonable compromise if the application is not prepared to
select higher sample rates.
.SS "Encodings"
-.sp
-.LP
An encoding parameter specifies the audiodata representation. u-Law encoding
corresponds to CCITT G.711, and is the standard for voice data used by
telephone companies in the United States, Canada, and Japan. A-Law encoding is
@@ -106,14 +92,10 @@ which sample values are directly proportional to audio signal voltages. Each
sample is a 2's complement number that represents a positive or negative
amplitude.
.SS "Precision"
-.sp
-.LP
Precision indicates the number of bits used to store each audio sample. For
instance, u-Law and A-Law data are stored with 8-bit precision. PCM data can be
stored at various precisions, though 16-bit is the most common.
.SS "Channels"
-.sp
-.LP
Multiple channels of audio can be interleaved at sample boundaries. A sample
frame consists of a single sample from each active channel. For example, a
sample frame of stereo 16-bit PCM data consists of 2 16-bit samples,
@@ -122,8 +104,6 @@ hardware to the maximum number of channels supported. If a mono signal is
played or recorded, it is mixed on the first two (usually the left and right)
channel only. Silence is mixed on the remaining channels.
.SS "Supported Formats"
-.sp
-.LP
The audio mixer supports the following audio formats:
.sp
.in +2
@@ -151,22 +131,12 @@ perform mixing. (This is done to allow for possible overflows to fit into
32-bits when mixing multiple streams together.) Hence, the maximum effective
precision is 24-bits.
.SH FILES
-.sp
-.ne 2
-.na
-\fB\fB/kernel/drv/audio\fR\fR
-.ad
-.RS 29n
-32-bit kernel driver module
-.RE
-
-.sp
.ne 2
.na
\fB\fB/kernel/drv/amd64/audio\fR\fR
.ad
.RS 29n
-64-bit x86 kernel driver module
+Device driver (x86)
.RE
.sp
@@ -175,7 +145,7 @@ precision is 24-bits.
\fB\fB/kernel/drv/sparcv9/audio\fR\fR
.ad
.RS 29n
-64-bit SPARC kernel driver module
+Device driver (SPARC)
.RE
.sp
@@ -184,12 +154,10 @@ precision is 24-bits.
\fB\fB/kernel/drv/audio.conf\fR\fR
.ad
.RS 29n
-\fBaudio\fR configuration file
+Driver configuration file
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for a description of the following attributes:
.sp
@@ -206,6 +174,4 @@ Interface Stability Uncommitted
.TE
.SH SEE ALSO
-.sp
-.LP
\fBioctl\fR(2), \fBattributes\fR(5), \fBaudio\fR(7I), \fBdsp\fR(7I)
diff --git a/usr/src/man/man7d/av1394.7d b/usr/src/man/man7d/av1394.7d
index 8fdf0652ce..53ce958979 100644
--- a/usr/src/man/man7d/av1394.7d
+++ b/usr/src/man/man7d/av1394.7d
@@ -3,37 +3,29 @@
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the
.\" fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
-.TH AV1394 7D "Apr 3, 2009"
+.TH AV1394 7D "Jan 10, 2020"
.SH NAME
av1394 \- 1394 audio/video driver
.SH SYNOPSIS
-.LP
.nf
\fBunit@GUID\fR
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBav1394\fR driver implements \fBiec61883\fR(7I) interfaces for IEEE 1394
compliant devices.
.SS "Asynchronous Transactions"
-.sp
-.LP
The driver allows applications to act as FCP controllers, but not FCP targets.
Only \fBIEC61883_FCP_CMD\fR requests can be sent with \fBwrite\fR(2). Only
\fBIEC61883_FCP_RESP\fR requests can be received with \fBread\fR(2).
.SS "Isochronous Transactions"
-.sp
-.LP
When the read/write method of is used for transmit, the driver is capable of
auto-detecting and transmitting SD-DVCR 525/60 and 625/50 streams. See
\fBiec61883\fR(7I) for details.
.SH FILES
-.sp
.ne 2
.na
-\fB\fB/dev/av/N/async\fR\fR
+\fB/dev/av/N/async\fR
.ad
.RS 29n
device node for asynchronous data
@@ -42,7 +34,7 @@ device node for asynchronous data
.sp
.ne 2
.na
-\fB\fB/dev/av/N/isoch\fR\fR
+\fB/dev/av/N/isoch\fR
.ad
.RS 29n
device node for isochronous data
@@ -51,33 +43,22 @@ device node for isochronous data
.sp
.ne 2
.na
-\fB\fBkernel/drv/sparcv9/av1394\fR\fR
+\fB/kernel/drv/sparcv9/av1394\fR
.ad
.RS 29n
-64-bit ELF kernel module
+Device driver (SPARC)
.RE
.sp
.ne 2
.na
-\fB\fBkernel/drv/av1394\fR\fR
+\fB/kernel/drv/amd64/av1394\fR
.ad
.RS 29n
-32-bit ELF kernel module
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBkernel/drv/amd64/av1394\fR\fR
-.ad
-.RS 29n
-64-bit ELF kernel module
+Device driver (x86)
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for a description of the following attributes:
.sp
@@ -94,8 +75,6 @@ Interface Stability Committed
.TE
.SH SEE ALSO
-.sp
-.LP
\fBread\fR(2), \fBwrite\fR(2), \fBattributes\fR(5), \fBhci1394\fR(7D),
\fBiec61883\fR(7I)
.sp
diff --git a/usr/src/man/man7d/bnxe.7d b/usr/src/man/man7d/bnxe.7d
index 78100d16eb..0696dc6c47 100644
--- a/usr/src/man/man7d/bnxe.7d
+++ b/usr/src/man/man7d/bnxe.7d
@@ -11,7 +11,7 @@
.\"
.\" Copyright (c) 2014 QLogic Corporation. All Rights Reserved
.\"
-.TH BNXE 7D "Jul 17, 2014"
+.TH BNXE 7D "Jan 10, 2020"
.SH NAME
bnxe \- QLogic NetXtreme II 10 Gigabit Ethernet Device Driver
@@ -21,7 +21,6 @@ bnxe \- QLogic NetXtreme II 10 Gigabit Ethernet Device Driver
.ad
.SH DESCRIPTION
-.LP
The
.B bnxe
Ethernet driver is a multi-threaded, loadable,
@@ -96,7 +95,7 @@ for Tx ring stats, and
"stats"
for general driver stats and version info.
.LP
-To get a list of all the individual statistics in these goups run:
+To get a list of all the individual statistics in these groups run:
.na
% kstat -m bnxe -i 0 -l
.ad
@@ -117,16 +116,7 @@ Character special device
/kernel/drv/bnxe.conf
.ad
.RS 16n
-Driver configuration file.
-.RE
-
-.sp
-.ne 2
-.na
-/kernel/drv/bnxe
-.ad
-.RS 16n
-32-bit i386 driver binary.
+Driver configuration file
.RE
.sp
@@ -135,7 +125,7 @@ Driver configuration file.
/kernel/drv/amd64/bnxe
.ad
.RS 16n
-64-bit i386 driver binary.
+Device driver (x86)
.RE
.sp
@@ -144,7 +134,7 @@ Driver configuration file.
/kernel/drv/sparcv9/bnxe
.ad
.RS 16n
-SPARC driver binary.
+Device driver (SPARC)
.RE
.SH SEE ALSO
diff --git a/usr/src/man/man7d/ccid.7d b/usr/src/man/man7d/ccid.7d
new file mode 100644
index 0000000000..86ada626e9
--- /dev/null
+++ b/usr/src/man/man7d/ccid.7d
@@ -0,0 +1,460 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2019 Joyent, Inc.
+.\"
+.Dd December 20, 2019
+.Dt CCID 7D
+.Os
+.Sh NAME
+.Nm ccid
+.Nd chip card interface device USB client class driver
+.Sh SYNOPSIS
+.In sys/usb/clients/ccid/uccid.h
+.Sh INTERFACE LEVEL
+.Sy Volatile
+.Pp
+The interfaces provided by this driver are private at this time and
+subject to change.
+It should not be relied upon.
+.Sh DESCRIPTION
+The
+.Nm
+driver is a USB CCID (chip card interface device) class device driver.
+.Pp
+The driver exposes interfaces that allow consumers to send and receive
+APDUs (application protocol data unit) to a given smart card that is
+plugged into a reader.
+The driver also provides interfaces to obtain status information, the
+ATR (answer to reset), and obtain exclusive access to the device.
+In addition, the system exposes control of CCID devices through
+.Xr cfgadm 1M .
+.Ss Supported Devices
+The CCID specification allows for readers to come in different flavors.
+These different flavors support different communication protocols and
+have different levels of automation for determining the protocol and
+transfers that are required.
+.Pp
+At this time, only the short APDU protocol is supported, which also works with
+readers using the extended APDU protocol.
+TPDU and character level readers are not supported by the driver.
+Readers in this category will still attach; however,
+I/O cannot be performed to them.
+.Pp
+In addition, at this time the driver does not support devices which
+require manually setting the clock and data rates to support an ICC.
+.Ss Device Model
+Each CCID class device provides a number of slots.
+Each slot may have an independent ICC (integrated circuit card or Smart
+Card) inserted into it.
+Each device, or reader, has its own directory under
+.Pa /dev/ccid
+based on its device number.
+Inside of each directory is a character device for each slot.
+A slot exists regardless of whether or not an ICC is inserted into it.
+As long as a CCID device is present in the system, its device nodes will
+be present.
+.Pp
+Slots are enumerated using this pattern:
+.Pa /dev/ccid/ccid%instance/slot%slot .
+.Pp
+For example, all the slots that belong to CCID instance 5 will be
+enumerated under the directory
+.Pa /dev/ccid/ccid5 .
+Slots are numbered starting at zero for each reader and increment from
+there.
+For example, the second physical slot would be numbered as slot one.
+If this were on CCID instance zero, then we would find a character
+device at:
+.Pa /dev/ccid/ccid0/slot1 .
+.Pp
+To enumerate all of the ccid devices present on the system, one could
+read all of the directories under
+.Pa /dev/ccid .
+To enumerate all of the slots on a device, one could read all of the
+device nodes under a particular CCID device, such as:
+.Pa /dev/ccid/ccid0 .
+The number of slots is also obtainable through various ioctls that will
+be discussed later on.
+It's important to note that while slot numbering will always be
+consistent for a given device, the CCID numbering is based on the driver
+instance.
+Therefore, it is possible for a device to change device numbers.
+To deal with this, symlinks based on other properties will be provided
+(for example, the USB serial number).
+.Pp
+All of the CCID devices in the system can also be listed by using the
+.Xr ccidadm 1M
+command.
+.Ss I/O Model
+To send and receive responses to commands, a program must open up the
+corresponding slot's device node.
+In many of the commands that use an ICC, there is a logical notion of
+state associated with the ICC that is mutated by performing commands on
+it.
+For example, a command might be issued that uses a PIN to unlock a slot
+or that selects a particular PIV applet for use.
+Because of this, all I/O to a given device must be performed inside the
+context of a transaction.
+When a program begins a transaction, it is guaranteed that no one else
+may send commands to the ICC.
+When a program is finished, it must explicitly end the transaction,
+which may have the side effect of resetting the ICC.
+If a program with an open transaction crashes or closes the file
+descriptor without taking other actions, then the transaction will be
+automatically closed.
+Without a transaction open, it will still be possible to issue ioctls
+that obtain the status of the slot and the reader.
+.Pp
+While in an active transaction, a program may send commands to a card.
+Sending a command and reading a response are done through the
+traditional
+.Xr read 2
+and
+.Xr write 2
+family of system calls.
+To submit a command, the program would issue a
+.Xr write 2
+family system call that contained the payload to send to the ICC.
+Once submitted, the call would return and the program would be able to
+issue a
+.Xr read 2
+system call to obtain the results.
+Once a command has been submitted, it is illegal to submit another one.
+The next command cannot be submitted until the response has been fully
+consumed.
+Similarly, if a command has not been submitted, one cannot issue a
+.Xr read 2
+system call to obtain results.
+Only a single thread may be blocked waiting to submit a command or
+read a response.
+.Pp
+To facilitate non-blocking operation, the underlying file descriptor may
+be opened with
+.Dv O_NONBLOCK .
+.Pp
+While a transaction is active,
+.Xr poll 2
+may be used to receive status information about the slot.
+The following events are used by
+.Nm :
+.Bl -tag -width POLLRDNORM
+.It Dv POLLOUT
+The device is ready to receive a command using
+.Xr write 2 .
+.It Dv POLLIN, POLLRDNORM
+The device has completed a command the results may be retrieved with
+.Xr read 2 .
+.It Dv POLLHUP
+The card has been removed from the slot.
+.It Dv POLLERR
+An hardware error has occurred, or the CCID reader has been disconnected.
+.El
+.Pp
+If
+.Xr poll 2
+is called while no transaction is active, none of these events will ever occur
+and
+.Xr poll 2
+may block indefinitely if no timeout is given.
+.Pp
+One important note is that readers with multiple slots often still only
+allow I/O a single command to be outstanding across all of the slots in
+the system.
+Because transactions are on a per-slot basis, it is still possible for a
+command submission to block even though one has a transaction open.
+.Pp
+While a transaction is open, various events can occur that cause a fatal
+error on the transaction.
+These include:
+.Bl -bullet -offset indent
+.It
+USB CCID reader removed
+.It
+ICC removed
+.It
+A fatal error while communicating to the device
+.It
+An administrator issued an ioctl to power off or reset the ICC
+.El
+.Pp
+Once such a fatal error has occurred, all new I/O will fail though it
+will still be possible to read any successfully completed commands.
+To clear the error state the program will need to end the transaction
+and begin a new one or close the file descriptor if the device has been
+removed.
+.Ss Opening Devices, Exclusive Access, and Performing I/O
+To perform I/O to a particular card, one must first open the slot of
+interest.
+Opening the slot requires that the process be in the global zone and
+that it have the privilege
+.Sy PRIV_SYS_DEVICES .
+The device node can be opened through the
+.Xr open 2
+or
+.Xr openat 2
+system calls.
+For programs that just want to query the slot status using the
+.Dv UCCID_CMD_STATUS
+command, opening the device node read-only is sufficient.
+All other uses require that the device be opened both for reading and
+writing
+.Po Dv O_RDWR Pc .
+.Pp
+Once the device has been opened, the program may issue ioctls to get
+status information.
+.Pp
+To perform general I/O to the card, a program must be in the context of
+a transaction as discussed in the
+.Sx I/O model
+section.
+To open a transaction, a program must issue the
+.Dv UCCID_CMD_TXN_BEGIN
+command through the
+.Xr ioctl 2
+system call.
+.Pp
+When a program is done, it must issue the
+.Dv UCCID_CMD_TXN_END
+command to release the transaction.
+As part of issuing the command, the program must determine a disposition
+of what it would like done with the card when it has completed.
+These options include leaving the ICC alone and resetting the ICC.
+For many use cases, such as those where a pin is entered or the ICC's
+state is mutated, a reset is the recommended option.
+If the program crashes or closes the file descriptor without issuing a
+transaction end, then the ICC will be reset.
+.Pp
+Please see the ioctl listing in the
+.Sx IOCTLS
+section for more information on the command structure.
+.Pp
+If a multi-threaded application opens a slot once and shares it among multiple
+threads performing I/O to that slot, there can still only be one transaction
+active or waiting on the slot shared by all threads.
+Acquiring another transaction on the same slot minor while another thread is
+already blocked waiting for one will return
+.Dv EINPROGRESS .
+If another transaction is already active,
+.Dv EINVAL
+will be returned.
+Consequently, all threads in a multi-threaded application share the transaction
+state and may issue writes, and read the results.
+The same applies to any other method of sharing an open file descriptor of a slot
+minor, be it by sharing the fd over a socket, a child process inheriting it from
+its parent during
+.Xr fork 2 ,
+even across calls to
+.Xr exec 2 .
+.Ss Device Status and ATR
+Once a slot has been opened, any caller may issue commands to get the
+status of the slot.
+This can also be used to obtain the ATR (answer to reset) of an ICC that
+is present on the slot, if it is known.
+.Pp
+While exclusive access is not required to issue these commands, there is
+no guarantee that they will not have changed between the time that the
+program issued the command and it obtains a transaction.
+.Pp
+To obtain information about the reader, slot, and the ATR, one should
+issue the
+.Dv UCCID_CMD_STATUS
+command.
+Please see the ioctl listing in the
+.Sx IOCTLS
+section for more information.
+.Sh IOCTLS
+This section lists the different commands that may be issued to a CCID
+device through the
+.Xr ioctl 2
+system call.
+.Ss Ic UCCID_CMD_STATUS
+This command is used to obtain the status of the slot.
+It may be used regardless of whether or not the caller has exclusive access.
+.Pp
+The
+.Ic UCCID_CMD_STATUS
+command uses the structure
+.Vt uccid_cmd_status_t ,
+the fields of which have the following meanings:
+.Bl -tag -width Fa
+.It Fa uint32_t ucs_version
+Indicates the current version of the structure.
+Should be set to
+.Dv UCCID_CURRENT_VERSION .
+.It Fa uint32_t ucs_status
+This value is ignored when issuing the command.
+On return, it will be filled in with various flags that describe the
+current status of the slot and the contents returned in the
+.Vt uccid_cmd_status_t .
+The following flags are defined:
+.Bl -tag -width Dv
+.It Dv UCCID_STATUS_F_CARD_PRESENT
+A card has been inserted into the slot of the CCID class device.
+.It Dv UCCID_STATUS_F_CARD_ACTIVE
+The inserted card has been successfully activated.
+This will only be set if the
+.Dv UCCID_STATUS_F_CARD_PRESENT
+flag is also set.
+.It Dv UCCID_STATUS_F_PRODUCT_VALID
+The contents of
+.Fa ucs_product
+are valid.
+.It Dv UCCID_STATUS_F_SERIAL_VALID
+The contents of
+.Fa ucs_serial
+are valid.
+.It Dv UCCID_STATUS_F_PARAMS_VALID
+The parameters returned in
+.Fa ucs_params
+are valid.
+.El
+.It Fa int32_t ucs_instance
+The instance number of the CCID device.
+.It Fa uint32_t ucs_slot
+The slot number currently in use.
+.It Fa uint8_t ucs_atr[UCCID_ATR_MAX]
+The ATR (answer to reset) of the card.
+.It Fa uint8_t ucs_atrlen
+The actual length of the ATR data.
+A length of 0 indicates that there is no ATR data.
+.It Fa int8_t ucs_product[256]
+The product string of the CCID device.
+.It Fa int8_t ucs_serial[256]
+The serial number of the CCID device.
+.It Fa ccid_class_descr_t ucs_class
+The CCID class descriptor of the CCID device.
+.It Fa uccid_prot_t ucs_prot
+The protocol in use by the ICC.
+This can be either
+.Dv UCCID_PROT_T0
+for the TPDU T=0 protocol or
+.Dv UCCID_PROT_T1
+for the TPDU T=1 protocol.
+.It Fa ccid_params_t ucs_params
+The CCID parameters available on the card.
+.El
+.Ss UCCID_CMD_TXN_BEGIN
+This command is used to begin a transaction.
+The command will block until exclusive access is available to the
+caller.
+If the caller does not wish to block, it should set the
+.Dv UCCID_TXN_DONT_BLOCK
+flag.
+.Pp
+The command uses the structure
+.Vt uccid_cmd_txn_begin_t
+with the following members:
+.Bl -tag -width Fa
+.It Fa uint32_t ucs_version
+Indicates the current version of the structure.
+Should be set to
+.Dv UCCID_CURRENT_VERSION .
+.It Fa uint32_t uct_flags
+Flags that impact the behavior of the command.
+The following flags are defined:
+.Bl -tag -width Dv
+.It Dv UCCID_TXN_DONT_BLOCK
+The command should not block for exclusive access.
+If exclusive access is not available, then the command will fail
+immediately.
+.El
+.Pp
+If an unknown flag is specified, an error will be returned.
+.El
+.Ss UCCID_CMD_TXN_END
+The
+.Dv UCCID_CMD_TXN_END
+command is used to end a transaction and relinquish exclusive access
+to the ICC.
+.Pp
+The command uses the structure
+.Vt uccid_cmd_txn_end_t
+with the following members:
+.Bl -tag -width Fa
+.It Fa uint32_t uct_version
+Indicates the current version of the structure.
+Should be set to
+.Dv UCCID_CURRENT_VERSION .
+.It Fa uint32_t uct_flags
+.Bl -tag -width Dv
+.It Dv UCCID_TXN_END_RESET
+The ICC should be reset at the end of the transaction.
+.It Dv UCCID_TXN_END_RELEASE
+The ICC should be released without being reset at the end of the
+transaction.
+.El
+.Pp
+Exactly one of these two flags must be specified.
+It is an error if neither flag or both flags are specified at the same
+time.
+If the device is closed without ending a transaction first, then the ICC
+will be reset.
+.El
+.Ss UCCID_CMD_ICC_MODIFY
+This command can be used to change the state of an ICC, if present.
+.Pp
+The command uses the structure
+.Vt uccid_cmd_icc_modify_t
+with the following members:
+.Bl -tag -width Fa
+.It Fa uint32_t uci_version
+Indicates the current version of the structure.
+Should be set to
+.Dv UCCID_CURRENT_VERSION .
+.It Fa uint32_t uci_action
+The action to be taken on the ICC.
+The following actions are defined:
+.Bl -tag -width Dv
+.It Dv UCCID_ICC_POWER_ON
+Power on the ICC.
+.It Dv UCCID_ICC_POWER_OFF
+Power off the ICC.
+.It Dv UCCID_ICC_WARM_RESET
+Perform a warm reset of the ICC.
+.El
+.El
+.Ss FIONREAD
+This command returns the size in bytes of a command response available
+for reading with
+.Xr read 2 .
+The size is returned in an
+.Vt int
+pointed to by the argument.
+.Sh SYSTEM CALLS
+This section lists the different system calls that may be issued to a
+CCID device.
+.Ss open
+.Ss close
+.Ss write
+.Ss read
+.Ss poll
+.Sh SEE ALSO
+.Xr ccidadm 1M ,
+.Xr cfgadm 1M ,
+.Xr close 2 ,
+.Xr ioctl 2 ,
+.Xr open 2 ,
+.Xr poll 2 ,
+.Xr read 2 ,
+.Xr write 2
+.Rs
+.%T Universal Serial Bus Device Class: Smart Card CCID
+.%O Revision 1.1
+.%D April 22, 2005
+.Re
+.Rs
+.%Q ISO/IEC
+.%B Identification Cards - Integrated Circuits
+.%N Part 3: Cards with contacts — Electrical interface and transmission protocols
+.%O ISO/IEC 7616-3:2006
+.%D 2006
+.Re
diff --git a/usr/src/man/man7d/coretemp.7d b/usr/src/man/man7d/coretemp.7d
index 2ac1008e55..d194d02fde 100644
--- a/usr/src/man/man7d/coretemp.7d
+++ b/usr/src/man/man7d/coretemp.7d
@@ -11,7 +11,7 @@
.\"
.\" Copyright 2019, Joyent, Inc.
.\"
-.Dd March 20, 2019
+.Dd January 10, 2020
.Dt CORETEMP 7D
.Os
.Sh NAME
@@ -28,7 +28,7 @@ Currently, the
.Nm
driver supports Intel Core family processors after Penryn
microarchitecture and Intel Atom processors starting with the Silvermont
-microarchitecure.
+microarchitecture.
.Pp
Temperature information is available to the system via the fault
management architecture
diff --git a/usr/src/man/man7d/dcam1394.7d b/usr/src/man/man7d/dcam1394.7d
index 22ad775895..c1323c8a8f 100644
--- a/usr/src/man/man7d/dcam1394.7d
+++ b/usr/src/man/man7d/dcam1394.7d
@@ -3,24 +3,19 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH DCAM1394 7D "May 19, 2004"
+.TH DCAM1394 7D "Jan 10, 2020"
.SH NAME
dcam1394 \- 1394-based digital camera (IIDC) driver
.SH SYNOPSIS
-.LP
.nf
\fB#include <sys/dcam/dcam1394_io.h>\fR
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBdcam1394\fR driver supports devices implementing the \fI1394 Trade
Association Digital Camera Specification\fR (also referred to as the IIDC
specification). Only a subset of the specification is supported.
.SH READING DATA
-.sp
-.LP
Isochronous data is read from the driver frame-by-frame and is maintained
within the driver in a ring buffer.
.sp
@@ -48,8 +43,6 @@ The size to allocate for the structure is determined by the video mode for
which the camera is configured. Possible values for the vid_mode field are
listed under DCAM1394_PARAM_VID_MODE below.
.SH IOCTL REQUESTS
-.sp
-.LP
The following ioctl requests are supported:
.sp
.ne 2
@@ -116,7 +109,7 @@ Gets a list of parameters associated with a camera. The argument is a pointer
to a \fBdcam1394_param_list_t\fR structure (described below). The parameter
list is accessed through macros defined below.
.sp
-The paramter list only supports Format 1 video formats.
+The parameter list only supports Format 1 video formats.
.RE
.sp
@@ -130,7 +123,7 @@ Sets a list of parameters associated with a camera. The argument is a pointer
to a \fBdcam1394_param_list_t structure\fR (described below). The parameter
list is accessed through macros defined below.
.sp
-The paramter list only supports Format 1 video formats.
+The parameter list only supports Format 1 video formats.
.RE
.sp
@@ -177,8 +170,6 @@ Reset frame sequence number.
.RE
.SH PARAMETER LIST ACCESS
-.sp
-.LP
The parameter list is initialized and access through macros. The data type for
the parameter list is \fBdcam1394_param_list_t\fR.
.sp
@@ -249,8 +240,6 @@ Indicates if a specific parameter is successfully set.
When no subparam value is required, the value DCAM1394_SUBPARAM_NONE may be
used.
.SH PARAMETERS
-.sp
-.LP
The following parameters may appear in the list:
.sp
.ne 2
@@ -896,10 +885,9 @@ DCAM1394_PARAM_BRIGHTNESS.
.RE
.SH DEVICE SPECIAL FILES
-.sp
.ne 2
.na
-\fB\fB/dev/dcam\fIN\fR\fR\fR
+\fB/dev/dcam\fIN\fR\fR
.ad
.RS 17n
Device node for isochronous input from camera.
@@ -908,36 +896,33 @@ Device node for isochronous input from camera.
.sp
.ne 2
.na
-\fB\fB/dev/dcamctl\fIN\fR\fR\fR
+\fB/dev/dcamctl\fIN\fR\fR
.ad
.RS 17n
Device node for camera control.
.RE
.SH FILES
-.sp
.ne 2
.na
-\fB\fBkernel/drv/sparcv9/dcam1394\fR\fR
+\fB/kernel/drv/sparcv9/dcam1394\fR
.ad
.sp .6
.RS 4n
-64-bit ELF kernel module.
+Device driver (SPARC)
.RE
.sp
.ne 2
.na
-\fB\fBkernel/drv/dcam1394\fR\fR
+\fB/kernel/drv/amd64/dcam1394\fR
.ad
.sp .6
.RS 4n
-32-bit ELF kernel module.
+Device driver (x86)
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -952,8 +937,6 @@ Interface Stability Evolving
.TE
.SH SEE ALSO
-.sp
-.LP
\fBattributes\fR(5), \fBhci1394\fR(7D)
.sp
.LP
diff --git a/usr/src/man/man7d/ecpp.7d b/usr/src/man/man7d/ecpp.7d
index 9c81264ef1..7d10e4ee9c 100644
--- a/usr/src/man/man7d/ecpp.7d
+++ b/usr/src/man/man7d/ecpp.7d
@@ -3,11 +3,10 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH ECPP 7D "May 13, 2017"
+.TH ECPP 7D "Jan 10, 2020"
.SH NAME
ecpp \- IEEE 1284 compliant parallel port driver
.SH SYNOPSIS
-.LP
.nf
#include <sys/types.h>
.fi
@@ -23,7 +22,6 @@ ecpp@unit-address
.fi
.SH DESCRIPTION
-.LP
The \fBecpp\fR driver provides a bi-directional interface to \fIIEEE 1284\fR
compliant devices as well as a forward single-directional interface to
Centronics devices. In addition to the Centronics protocol, the \fBecpp\fR
@@ -48,7 +46,6 @@ in \fBecpp.conf\fR.
The \fBecpp\fR driver is an \fIexclusive-use\fR device, meaning that if the
device is already open, subsequent opens fail with \fBEBUSY\fR.
.SS "Default Operation"
-.LP
Each time the \fBecpp\fR device is opened, the device is marked as \fBEBUSY\fR
and the configuration variables are set to their default values. The
\fBwrite_timeout\fR period is set to 90 seconds.
@@ -71,8 +68,7 @@ the \fBwrite_timeout\fR values through the \fBECPPIOC_SETPARMS\fR
\fBioctl\fR(2) call. For mode negotiation to be successful, both the host
workstation and the peripheral must support the requested mode.
.SS "Tunables"
-.LP
- Characteristics of the \fBecpp\fR driver may be tuned by the variables
+Characteristics of the \fBecpp\fR driver may be tuned by the variables
described in \fB/kernel/drv/ecpp.conf\fR. These variables are read by the
kernel during system startup. To tune the variables, edit the \fBecpp.conf\fR
file and invoke \fBupdate_drv\fR(1M) to have the kernel read the file again.
@@ -83,12 +79,10 @@ will not operate with the parallel port operating in a fast handshaking mode.
If printing problems occur, set "fast-centronics" and "fast-1284-compatible" to
"false." See \fB/kernel/drv/ecpp.conf\fR for more information.
.SS "Read/Write Operation"
-.LP
The \fBecpp\fR driver is a full duplex STREAMS device driver. While an
application is writing to an \fIIEEE 1284\fR compliant device, another thread
may read from it.
.SS "Write Operation"
-.LP
A \fBwrite\fR(2) operation returns the number of bytes successfully written to
the stream head. If a failure occurs while a Centronics device is transferring
data, the content of the status bits will be captured at the time of the error
@@ -96,7 +90,6 @@ and can be retrieved by the application program using the \fBBPPIOC_GETERR\fR
\fBioctl\fR(2) call. The captured status information is overwritten each time
an attempted transfer or a \fBBPPIOC_TESTIO\fR \fBioctl\fR(2) occurs.
.SS "Read Operation"
-.LP
If a failure or error condition occurs during a \fBread\fR(2), the number of
bytes successfully read is returned (short read). When attempting to read a
port that has no data currently available, \fBread\fR(2) returns \fB0\fR if
@@ -104,7 +97,6 @@ port that has no data currently available, \fBread\fR(2) returns \fB0\fR if
\fB-1\fR and sets errno to \fBEAGAIN.\fR If \fBO_NDELAY\fR and \fBO_NONBLOCK\fR
are clear, \fBread\fR(2) blocks until data become available.
.SH IOCTLS
-.LP
The \fBioctl\fR(2) calls described below are supported. Note that when
\fBecpp\fR is transferring data, the driver waits until the data has been sent
to the device before processing the \fBioctl\fR(2) call.
@@ -340,7 +332,7 @@ bits are reserved. Reading reserved bits always return 1. An attempt to write
\fB\fB/dev/lp\fIN\fR\fR\fR
.ad
.RS 19n
-Solaris x86 only. (Backwards compatibility with former \fBlp\fR(7D) devices.)
+x86 only. (Backwards compatibility with former \fBlp\fR(7D) devices.)
.RE
.sp
@@ -355,61 +347,31 @@ Solaris x86 only. (Backwards compatibility with former \fBlp\fR(7D) devices.)
.SH FILES
.ne 2
.na
-\fB\fBkernel/drv/ecpp\fR\fR
-.ad
-.sp .6
-.RS 4n
-32-bit ELF kernel module
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBkernel/drv/sparcv9/ecpp\fR\fR
-.ad
-.sp .6
-.RS 4n
-64-bit SPARC ELF kernel module
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBkernel/drv/amd64/ecpp\fR\fR
+\fB/kernel/drv/sparcv9/ecpp\fR
.ad
.sp .6
.RS 4n
-64-bit x86 ELF kernel module
+Device driver (SPARC)
.RE
.sp
.ne 2
.na
-\fB\fBkernel/drv/ecpp.conf\fR\fR
+\fB/kernel/drv/amd64/ecpp\fR
.ad
.sp .6
.RS 4n
-driver configuration file
+Device driver (x86)
.RE
.sp
.ne 2
.na
-\fB\fBkernel/drv/sparcv9/ecpp.conf\fR\fR
+\fB/kernel/drv/ecpp.conf\fR
.ad
.sp .6
.RS 4n
-driver configuration file for 64-bit SPARC
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBkernel/drv/amd64/ecpp.conf\fR\fR
-.ad
-.sp .6
-.RS 4n
-driver configuration file for 64-bit x86
+Driver configuration file
.RE
.SH ERRORS
@@ -468,7 +430,6 @@ peripheral error.
.RE
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -487,7 +448,6 @@ Interface stability Evolving
.TE
.SH SEE ALSO
-.LP
\fBmodload\fR(1M), \fBmodunload\fR(1M), \fBupdate_drv\fR(1M), \fBioctl\fR(2),
\fBopen\fR(2), \fBread\fR(2), \fBwrite\fR(2), \fBattributes\fR(5),
\fBbpp\fR(7D), \fBusbprn\fR(7D), \fBprnio\fR(7I), \fBstreamio\fR(7I)
diff --git a/usr/src/man/man7d/ehci.7d b/usr/src/man/man7d/ehci.7d
index a1a9a3efb8..b9ba26cf3d 100644
--- a/usr/src/man/man7d/ehci.7d
+++ b/usr/src/man/man7d/ehci.7d
@@ -3,17 +3,15 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH EHCI 7D "May 13, 2017"
+.TH EHCI 7D "Jan 10, 2020"
.SH NAME
ehci \- Enhanced host controller driver
.SH SYNOPSIS
-.LP
.nf
\fBusb@unit-address\fR
.fi
.SH DESCRIPTION
-.LP
The \fBehci\fR driver is a USBA (Solaris USB Architecture) compliant nexus
driver that supports the Enhanced Host Controller Interface Specification 2.0,
an industry standard developed by Intel.
@@ -35,24 +33,15 @@ controller should be routed to the companion USB 1.1 host controllers. (OHCI or
UHCI host controller).
.sp
.LP
-The \fBehci\fR supports bulk, interrupt, control and iso chronous transfers
+The \fBehci\fR supports bulk, interrupt, control and isochronous transfers
(on USB1.\fIx\fR devices behind a USB2.0 hub).
.SH FILES
.ne 2
.na
-\fB\fB/kernel/drv/ehci\fR\fR
-.ad
-.RS 28n
-32-bit ELF 86 kernel module
-.RE
-
-.sp
-.ne 2
-.na
\fB\fB/kernel/drv/sparcv9/ehci\fR\fR
.ad
.RS 28n
-64-bit SPARC ELF kernel module
+Device driver (SPARC)
.RE
.sp
@@ -61,7 +50,7 @@ The \fBehci\fR supports bulk, interrupt, control and iso chronous transfers
\fB\fB/kernel/drv/amd64/ehci\fR\fR
.ad
.RS 28n
-64-bit x86 ELF kernel module
+Device driver (x86)
.RE
.sp
@@ -74,7 +63,6 @@ Driver configuration file
.RE
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -89,7 +77,6 @@ Architecture SPARC, x86, PCI-based systems
.TE
.SH SEE ALSO
-.LP
\fBadd_drv\fR(1M), \fBprtconf\fR(1M), \fBrem_drv\fR(1M), \fBupdate_drv\fR(1M),
\fBattributes\fR(5), \fBhubd\fR(7D), \fBuhci\fR(7D), \fBohci\fR(7D),
\fBusba\fR(7D)
@@ -112,7 +99,6 @@ Architecture SPARC, x86, PCI-based systems
.LP
\fIhttp://www.intel.com/technology/usb/ehcispec.htm\fR
.SH DIAGNOSTICS
-.LP
In addition to being logged, the following messages may appear on the system
console. All messages are formatted in the following manner:
.sp
@@ -204,7 +190,7 @@ comment out a property in ehci.conf. (x86 only).
The driver is unable to take control of the EHCI hardware from the
system's BIOS and aborts the attach. High speed (USB 2.0) support is disabled.
In this case, all USB devices run at full/low speed. Contact your system vendor
-or your system administror for possible changes in BIOS settings. You can
+or your system administrator for possible changes in BIOS settings. You can
disable a property in \fBehci.conf\fR to ignore this failure. (x86 only.)
.RE
diff --git a/usr/src/man/man7d/elxl.7d b/usr/src/man/man7d/elxl.7d
index cc49101dbd..a6d3d3bf64 100644
--- a/usr/src/man/man7d/elxl.7d
+++ b/usr/src/man/man7d/elxl.7d
@@ -20,7 +20,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
.\"
-.Dd "Aug 7, 2014"
+.Dd "Jan 10, 2020"
.Dt ELXL 7D
.Os
.Sh NAME
@@ -60,7 +60,7 @@ media options on the device:
.Lp
.Bl -tag -compact -offset indent -width Sy
.It Sy mii
-Media Indendent Interface (MII), also 100BASE-TX
+Media Independent Interface (MII), also 100BASE-TX
.It Sy tp-hdx
10 Mbps twisted pair, half-duplex
.It Sy tp-fdx
diff --git a/usr/src/man/man7d/fcoe.7d b/usr/src/man/man7d/fcoe.7d
index 3a8e290139..a3354b5649 100644
--- a/usr/src/man/man7d/fcoe.7d
+++ b/usr/src/man/man7d/fcoe.7d
@@ -3,50 +3,37 @@
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the
.\" fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
-.TH FCOE 7D "Mar 18, 2009"
+.\" Copyright 2020 Peter Tribble.
+.TH FCOE 7D "Jan 10, 2020"
.SH NAME
fcoe \- fibre channel over Ethernet transport driver
.SH DESCRIPTION
-.sp
-.LP
The \fBfcoe\fR driver is a pseudo nexus driver which supports the
-transportation of FCoE encapsualted frames. FCoE Ethernet frame will
+transportation of FCoE encapsulated frames. FCoE Ethernet frame will
encapsulate the raw Fibre Channel frame.
.sp
.LP
The \fBfcoe\fR driver interfaces with FCoE target mode device driver,
\fBfcoet\fR(7D).
.SH FILES
-.sp
-.ne 2
-.na
-\fB\fB/kernel/drv/fcoe\fR\fR
-.ad
-.RS 26n
-32-bit ELF kernel module (x86)
-.RE
-
-.sp
.ne 2
.na
-\fB\fB/kernel/drv/amd64/fcoe\fR\fR
+\fB/kernel/drv/amd64/fcoe\fR
.ad
.RS 26n
-64-bit ELF kernel module (x86)
+Device driver (x86)
.RE
.sp
.ne 2
.na
-\fB\fBkernel/drv/sparcv\fR\fR
+\fB/kernel/drv/sparcv9/fcoe\fR
.ad
.RS 26n
-64-bit ELF kernel module (SPARC)
+Device driver (SPARC)
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for a description of the following attribute:
.sp
@@ -61,8 +48,6 @@ Architecture SPARC, x86
.TE
.SH SEE ALSO
-.sp
-.LP
\fBdriver.conf\fR(4), \fBattributes\fR(5), \fBfcoet\fR(7D)
.sp
.LP
diff --git a/usr/src/man/man7d/fcoei.7d b/usr/src/man/man7d/fcoei.7d
index 1b08d363f1..2835290d9e 100644
--- a/usr/src/man/man7d/fcoei.7d
+++ b/usr/src/man/man7d/fcoei.7d
@@ -2,11 +2,10 @@
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following
.\" below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH FCOEI 7D "Sep 11, 2009"
+.TH FCOEI 7D "Jan 10, 2020"
.SH NAME
fcoei \- Fibre Channel Over Ethernet Initiator Mode Driver
.SH SYNOPSIS
-.sp
.in +2
.nf
fcoei@port,0
@@ -14,8 +13,6 @@ fcoei@port,0
.in -2
.SH DESCRIPTION
-.sp
-.LP
The \fBfcoei\fR driver is a pseudo device driver which encapsulates the raw
Fibre Channel frames into FCoE ethernet frames, or decapsulates FC frames from
FCoE ethernet frames. The supported FC frames include extended/basic link
@@ -25,36 +22,24 @@ services, common transport frames and initiator mode FCP frames.
The \fBfcoei\fR driver interfaces with the Sun Fibre Channel port driver,
\fBfp\fR(7D), and the FCoE transport driver, \fBfcoe\fR(7D).
.SH FILES
-.sp
.ne 2
.na
-\fB\fB/kernel/drv/fcoei\fR\fR
+\fB/kernel/drv/amd64/fcoei\fR
.ad
.RS 27n
-32-bit ELF kernel module (x86)
+Device driver (x86)
.RE
.sp
.ne 2
.na
-\fB\fB/kernel/drv/amd64/fcoei\fR\fR
+\fB/kernel/drv/sparcv9/fcoei\fR
.ad
.RS 27n
-64-bit ELF kernel module (x86)
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBkernel/drv/sparcv/fcoei\fR\fR
-.ad
-.RS 27n
-64-bit ELF kernel module (SPARC)
+Device driver (SPARC)
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for a description of the following attribute:
.sp
@@ -69,8 +54,6 @@ Architecture SPARC, x86
.TE
.SH SEE ALSO
-.sp
-.LP
\fBdriver.conf\fR(4), \fBattributes\fR(5), \fBfcoe\fR(7D), \fBfcoet\fR(7D),
\fBfp\fR(7D)
.sp
diff --git a/usr/src/man/man7d/fcoet.7d b/usr/src/man/man7d/fcoet.7d
index 53da09f911..dd604f360b 100644
--- a/usr/src/man/man7d/fcoet.7d
+++ b/usr/src/man/man7d/fcoet.7d
@@ -3,51 +3,37 @@
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
-.TH FCOET 7D "Mar 18, 2009"
+.TH FCOET 7D "Jan 10, 2020"
.SH NAME
fcoet \- fibre channel over Ethernet target mode driver
.SH DESCRIPTION
-.sp
-.LP
The \fBfcoet\fR driver is a pseudo device driver which encapsulates the raw
Fibre Channel frames into FCoE Ethernet frames, or decapsulates FC frames from
FCoE Ethernet frames. The supported FC frames contain extended/basic link
services, common transport frames and target mode FCP frames.
.sp
.LP
-The \fBfcoet\fR driver interfaces with COMSTAR FC transport driver,\fBfct\fR,
+The \fBfcoet\fR driver interfaces with COMSTAR FC transport driver, \fBfct\fR,
and FCoE transport driver, \fBfcoe\fR(7D).
.SH FILES
-.sp
-.ne 2
-.na
-\fB\fB/kernel/drv/fcoet\fR\fR
-.ad
-.RS 29n
-32-bit ELF kernel module (x86)
-.RE
-
-.sp
.ne 2
.na
-\fB\fB/kernel/drv/amd64/fcoet\fR\fR
+\fB/kernel/drv/amd64/fcoet\fR
.ad
.RS 29n
-64-bit ELF kernel module (x86)
+Device driver (x86)
.RE
.sp
.ne 2
.na
-\fB\fB/kernel/drv/sparcv9/fcoet\fR\fR
+\fB/kernel/drv/sparcv9/fcoet\fR
.ad
.RS 29n
-64-bit ELF kernel module (SPARC)
+Device driver (SPARC)
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for a description of the following attributes:
.sp
@@ -62,8 +48,6 @@ Architecture SPARC, x86
.TE
.SH SEE ALSO
-.sp
-.LP
\fBdriver.conf\fR(4), \fBattributes\fR(5), \fBfcoe\fR(7D)
.sp
.LP
diff --git a/usr/src/man/man7d/hci1394.7d b/usr/src/man/man7d/hci1394.7d
index 8eea798743..34ac56762f 100644
--- a/usr/src/man/man7d/hci1394.7d
+++ b/usr/src/man/man7d/hci1394.7d
@@ -3,43 +3,29 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH HCI1394 7D "Aug 30, 2005"
+.TH HCI1394 7D "Jan 10, 2020"
.SH NAME
hci1394 \- 1394 OpenHCI host controller driver
.SH SYNOPSIS
-.LP
.nf
\fBfirewire@unit-address\fR
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBhci1394\fR host controller driver is an IEEE 1394 compliant nexus driver
that supports the \fI1394 Open Host Controller Interface Specification 1.0\fR,
an industry standard developed by Sun, Apple, Compaq, Intel, Microsoft,
-National Semconductor, and Texas Instruments. The \fBhci1394\fR driver supports
+National Semiconductor, and Texas Instruments. The \fBhci1394\fR driver supports
asynchronous transfers, isochronous transfers, and bus reset management. The
\fBhci1394\fR driver also supports the nexus device control interface.
.SH FILES
-.sp
.ne 2
.na
\fB\fB/kernel/drv/sparcv9/hci1394\fR\fR
.ad
.sp .6
.RS 4n
-64-bit SPARC ELF kernel module
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fB/kernel/drv/hci1394\fR\fR
-.ad
-.sp .6
-.RS 4n
-32-bit x86 ELF kernel module
+Device driver (SPARC)
.RE
.sp
@@ -49,12 +35,10 @@ asynchronous transfers, isochronous transfers, and bus reset management. The
.ad
.sp .6
.RS 4n
-64-bit x86 ELF kernel module
+Device driver (x86)
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for a description of the following attributes:
.sp
@@ -71,8 +55,6 @@ Interface Stability Unstable
.TE
.SH SEE ALSO
-.sp
-.LP
\fBattributes\fR(5), \fBieee1394\fR(7D)
.sp
.LP
diff --git a/usr/src/man/man7d/i40e.7d b/usr/src/man/man7d/i40e.7d
index f025fba01a..81ab257f6e 100644
--- a/usr/src/man/man7d/i40e.7d
+++ b/usr/src/man/man7d/i40e.7d
@@ -11,7 +11,7 @@
.\"
.\" Copyright (c) 2018 Joyent, Inc.
.\"
-.Dd May 23, 2018
+.Dd Jan 10, 2020
.Dt I40E 7D
.Os
.Sh NAME
@@ -64,7 +64,7 @@ is recommended.
Each instance is assigned a unique ascending integer identifier.
A device which has multiple ports may appear to the system as separate
instances.
-The system does not provide a guarnatee on how these will be presented.
+The system does not provide a guarantee on how these will be presented.
Using this instance identifier, one can determine the exact character-special
file to open.
For example, the first instance enumerated in the system, with id 0, would be
@@ -150,7 +150,7 @@ Maximum:
.Bd -filled
The
.Sy mr_enable
-proeprty determines whether or not support for multiple rings is enabled
+property determines whether or not support for multiple rings is enabled
for the device.
The default is always to enable them.
It is not recommended to to disable them.
@@ -232,7 +232,7 @@ Maximum:
The
.Sy tx_hcksum_enable
property controls whether or not the device enables support for hardware
-checksuming of outgoing packets.
+checksumming of outgoing packets.
The default is to always enable support for this.
Turning it off will increase latency and decrease throughput when transmitting
packets, but should be done if a hardware bug is suspected.
@@ -248,7 +248,7 @@ Maximum:
The
.Sy rx_hcksum_enable
property controls whether or not the device enables support for hardware
-checksuming of incoming packets.
+checksumming of incoming packets.
The default is to always enable support for this.
Turning it off will increase latency and decrease throughput when receiving
packets, but should be done if a hardware bug is suspected.
@@ -260,11 +260,11 @@ Minimum:
Maximum:
.Sy INT32_MAX |
Runtime Property:
-.Sy _rx_dma_treshold
+.Sy _rx_dma_threshold
.Ed
.Bd -filled
The
-.Sy rx_dma_treshold
+.Sy rx_dma_threshold
indicates the size in bytes of a received frame, including all of its
headers, at which the driver should not copy the frame but instead bind
DMA memory.
@@ -300,12 +300,10 @@ systems at this time.
.Bl -tag -width Pa
.It Pa /dev/net/i40e*
Per-instance character device.
-.It Pa /kernel/drv/i40e
-32-bit device driver (x86).
.It Pa /kernel/drv/amd64/i40e
-64-bit device driver (x86).
+Device driver (x86)
.It Pa /kernel/drv/i40e.conf
-Driver configuration file.
+Driver configuration file
.El
.Sh SEE ALSO
.Xr dladm 1M ,
diff --git a/usr/src/man/man7d/ieee1394.7d b/usr/src/man/man7d/ieee1394.7d
index bcdf216811..07cf605b11 100644
--- a/usr/src/man/man7d/ieee1394.7d
+++ b/usr/src/man/man7d/ieee1394.7d
@@ -1,27 +1,27 @@
'\" te
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright 2020 Peter Tribble.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH IEEE1394 7D "May 13, 2017"
+.TH IEEE1394 7D "Jan 10, 2020"
.SH NAME
-ieee1394, firewire, 1394 \- Solaris IEEE-1394 Architecture
+ieee1394, firewire, 1394 \- illumos IEEE-1394 Architecture
.SH DESCRIPTION
-.LP
IEEE-1394 provides a means for interconnecting devices in computer and home
entertainment systems. (The IEEE-1394 architecture is also known as Firewire,
an Apple Computer trademark, and i.Link, a Sony trademark). The most common
IEEE-1394 devices are digital camcorders, mass-storage devices and cameras
-(including webcam-type devices). For more information on USB, refer to the 1394
-Trade Association website at http://\fIwww.1394ta.org\fR.
+(including webcam-type devices). For more information on IEEE-1394, refer to the
+1394 Trade Association website at http://\fIwww.1394ta.org\fR.
.sp
.LP
-The Solaris IEEE-1394 architecture supports up to 63 hot-pluggable IEEE-1394
+The illumos IEEE-1394 architecture supports up to 63 hot-pluggable IEEE-1394
devices per IEEE-1394 bus. The maximum data transfer rate is 400 Mbits,
depending on the capabilities of the attached device.
.sp
.LP
-The Solaris IEEE-1394 architecture supports devices implementing a number of
+The illumos IEEE-1394 architecture supports devices implementing a number of
different specifications. The basic behavior of the IEEE-1394 bus is described
in the \fIIEEE 1394-1995\fR and \fIIEEE 1394a-2000\fR specifications.
.sp
@@ -33,11 +33,8 @@ devices implementing the \fIANSI SBP-2\fR specification are supported. Digital
cameras implementing the 1394 Trade Association 1394-based Digital Camera
(IIDC) specification are supported.
.SH FILES
-.LP
Listed below are drivers and modules which either utilize or are utilized by
-the Solaris IEEE-1394 architecture. Drivers in \fB/kernel/drv\fR are 32 bit
-drivers (only). Drivers in \fB/kernel/drv/sparcv9\fR or \fB/kernel/drv/amd64\fR
-are 64 bit drivers.
+the illumos IEEE-1394 architecture.
.sp
.sp
@@ -47,9 +44,9 @@ c | c
l | l .
SUPPORT MODULE(S) FUNCTION
_
-/kernel/misc/[sparcv9|amd64/]s1394 IEEE-1394 framework
+/kernel/misc/[sparcv9|amd64]/s1394 IEEE-1394 framework
_
-/kernel/misc/[sparcv9|amd64/]sbp2 Serial Bus Protocol-2 (SBP-2)
+/kernel/misc/[sparcv9|amd64]/sbp2 Serial Bus Protocol-2 (SBP-2)
.TE
.sp
@@ -61,13 +58,11 @@ c | c
l | l .
TARGET DRIVER DEVICE CLASS
_
-/kernel/drv/[sparcv9|amd64/]s1394 IEEE-1394 framework
-_
-/kernel/drv/[sparcv9|amd64/]scsa1394 mass storage class
+/kernel/drv/[sparcv9|amd64]/scsa1394 mass storage class
_
-/kernel/drv/[sparcv9|amd64/]av1394 camcorder (AV/C) class
+/kernel/drv/[sparcv9|amd64]/av1394 camcorder (AV/C) class
_
-/kernel/drv/[sparcv9|amd64/]dcam1394 digital camera (IIDC) class
+/kernel/drv/[sparcv9|amd64]/dcam1394 digital camera (IIDC) class
.TE
.sp
@@ -79,11 +74,10 @@ c | c
l | l .
HOST CONTROLLER INTERFACE DRIVER(S) DEVICE
_
-/kernel/drv/[sparcv9|amd64/]hci1394 Open HCI
+/kernel/drv/[sparcv9|amd64]/hci1394 Open HCI
.TE
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -98,7 +92,6 @@ Architecture PCI-based systems
.TE
.SH SEE ALSO
-.LP
\fBattributes\fR(5), \fBav1394\fR(7D), \fBdcam1394\fR(7D), \fBhci1394\fR(7D),
\fBscsa1394\fR(7D), \fBiec61883\fR(7I)
.sp
@@ -108,6 +101,5 @@ Architecture PCI-based systems
.LP
\fIIEEE 1394\fR Specification - 1394 Trade Association, 1995
.SH NOTES
-.LP
Booting from IEEE-1394 mass-storage devices is not supported, but may be
possible if supported by the BIOS of the computer system.
diff --git a/usr/src/man/man7d/ixgbe.7d b/usr/src/man/man7d/ixgbe.7d
index 324ced1012..5b7f8e25e2 100644
--- a/usr/src/man/man7d/ixgbe.7d
+++ b/usr/src/man/man7d/ixgbe.7d
@@ -5,17 +5,15 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH IXGBE 7D "Apr 10, 2016"
+.TH IXGBE 7D "Jan 10, 2020"
.SH NAME
ixgbe \- Intel 10Gb PCI Express NIC Driver
.SH SYNOPSIS
-.LP
.nf
\fB/dev/ixgbe*\fR
.fi
.SH DESCRIPTION
-.LP
The \fBixgbe\fR 10 Gigabit Ethernet driver is a multi-threaded, loadable,
clonable, GLD-based STREAMS driver supporting the Data Link Provider Interface,
\fBdlpi\fR(7P), on Intel 10-Gigabit PCI Express Ethernet controllers.
@@ -55,7 +53,6 @@ Intel Ethernet Controller X550 Family
The \fBixgbe\fR driver and hardware support auto-negotiation, a protocol
specified by the \fIIEEE 802.3ae\fR specification.
.SH APPLICATION PROGRAMMING INTERFACE
-.LP
The cloning character-special device, \fB/dev/ixgbe\fR, is used to access all
Intel 10-Gigabit PCI Express Ethernet devices installed within the system.
.sp
@@ -124,7 +121,6 @@ Once in the DL_ATTACHED state, you must send a DL_BIND_REQ to associate a
particular SAP with the stream.
.RE
.SH CONFIGURATION
-.LP
By default, the \fBixgbe\fR driver performs auto-negotiation to select the link
speed and mode. Link speed and mode can only be 10000 Mbps full-duplex for fiber, and only 10000, 1000, or 100 Mbps full-duplex for copper. See the
\fIIEEE802.3\fR standard for more information.
@@ -140,19 +136,10 @@ Special character device.
.sp
.ne 2
.na
-\fB\fB/kernel/drv/ixgbe\fR\fR
-.ad
-.RS 29n
-32-bit device driver (x86).
-.RE
-
-.sp
-.ne 2
-.na
\fB\fB/kernel/drv/amd64/ixgbe\fR\fR
.ad
.RS 29n
-64-bit device driver (x86).
+Device driver (x86)
.RE
.sp
@@ -161,7 +148,7 @@ Special character device.
\fB\fB/kernel/drv/sparcv9/ixgbe\fR\fR
.ad
.RS 29n
-64-bit device driver (SPARC).
+Device driver (SPARC)
.RE
.sp
@@ -170,11 +157,10 @@ Special character device.
\fB\fB/kernel/drv/ixgbe.conf\fR\fR
.ad
.RS 29n
-Configuration file.
+Driver configuration file
.RE
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -191,7 +177,6 @@ Interface Stability Committed
.TE
.SH SEE ALSO
-.LP
\fBdladm\fR(1M), \fBnetstat\fR(1M), \fBdriver.conf\fR(4), \fBattributes\fR(5),
\fBstreamio\fR(7I), \fBdlpi\fR(7P)
.sp
@@ -205,4 +190,4 @@ Interface Stability Committed
\fINetwork Interfaces Programmer's Guide\fR
.sp
.LP
-\fIIEEE 802.3ae Specificiation\fR, IEEE - 2002
+\fIIEEE 802.3ae Specification\fR, IEEE - 2002
diff --git a/usr/src/man/man7d/ntwdt.7d b/usr/src/man/man7d/ntwdt.7d
index d5e37c1edc..553c8c7a38 100644
--- a/usr/src/man/man7d/ntwdt.7d
+++ b/usr/src/man/man7d/ntwdt.7d
@@ -3,18 +3,15 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH NTWDT 7D "Feb 05, 2005"
+.TH NTWDT 7D "Jan 10, 2020"
.SH NAME
ntwdt \- Netra\(embased application watchdog timer driver
.SH SYNOPSIS
-.LP
.nf
/dev/ntwdt
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBntwdt\fR driver is a multithreaded, loadable, non-STREAMS pseudo driver
that provides an application with an interface for controlling a system
watchdog timer.
@@ -23,13 +20,9 @@ watchdog timer.
The \fBntwdt\fR driver implements a \fBvirtual watchdog timer\fR that a
privileged application (Effective UID == 0) controls via IOCTLs.
.SH CONFIGURATION
-.sp
-.LP
You configure the \fBntwdt\fR driver by modifying the contents of the
\fBntwdt.conf\fR file.
.SH ERRORS
-.sp
-.LP
An \fBopen()\fR fails if:
.sp
.ne 2
@@ -59,7 +52,6 @@ Effective user ID is not zero.
.RE
.SH FILES
-.sp
.ne 2
.na
\fB/dev/ntwdt\fR
@@ -71,24 +63,22 @@ Special character device.
.sp
.ne 2
.na
-\fBkernel/drv/sparcv9/ntwdt\fR
+\fB/kernel/drv/sparcv9/ntwdt\fR
.ad
.RS 28n
-SPARC ntwdt driver binary.
+Device driver (SPARC)
.RE
.sp
.ne 2
.na
-\fBkernel/drv/ntwdt.conf\fR
+\fB/kernel/drv/ntwdt.conf\fR
.ad
.RS 28n
-Driver configuraton file.
+Driver configuration file
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -103,8 +93,6 @@ Architecture SPARC
.TE
.SH SEE ALSO
-.sp
-.LP
\fBdriver.conf\fR(4), \fBattributes\fR(5)
.sp
.LP
diff --git a/usr/src/man/man7d/pchtemp.7d b/usr/src/man/man7d/pchtemp.7d
index d89f09ab90..00635cb6cd 100644
--- a/usr/src/man/man7d/pchtemp.7d
+++ b/usr/src/man/man7d/pchtemp.7d
@@ -11,7 +11,7 @@
.\"
.\" Copyright 2019 Joyent, Inc.
.\"
-.Dd April 26, 2019
+.Dd January 10, 2020
.Dt PCHTEMP 7D
.Os
.Sh NAME
@@ -24,7 +24,7 @@ The
.Nm
driver provides the system the ability to read the digital temperature
sensor found on several Intel platform controller hub (PCH) chipsets.
-The following chipsets are supported which cover most Intel Core familiy
+The following chipsets are supported which cover most Intel Core family
(non-Atom) CPUs starting with the Haswell generation:
.Bl -dash
.It
diff --git a/usr/src/man/man7d/pcn.7d b/usr/src/man/man7d/pcn.7d
index 087ca63179..f78375239d 100644
--- a/usr/src/man/man7d/pcn.7d
+++ b/usr/src/man/man7d/pcn.7d
@@ -24,27 +24,24 @@
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
.\" Portions Copyright (c) 2007 by Sun Microsystems, Inc. All Rights Reserved.
-.TH "PCN" "7D" "Sep 16, 2011"
+.TH "PCN" "7D" "Jan 10, 2020"
.
.SH "NAME"
\fBpcn\fR \- PCnet Ethernet device driver
.SH "SYNOPSIS"
-.LP
.nf
\fB/dev/pcn\fR
.fi
.SH "DESCRIPTION"
-.sp
-.LP
The \fBpcn\fR driver is a multi\-threaded, loadable, clonable GLDv3\-based
STREAMS driver supporting the Data Link Provider Interface \fBdlpi\fR(7P) for
-the AMD PCnet family of Ethernet controllers\.
+the AMD PCnet family of Ethernet controllers.
.SH "APPLICATION PROGRAMMING INTERFACE"
The \fBpcn\fR driver can be used as either a "style 1" or a "style 2" Data Link
-Service Provider\. Physical points of attachment (PPAs) are interpreted as the
+Service Provider. Physical points of attachment (PPAs) are interpreted as the
instance number of the \fBpcn\fR controller as assigned by the
-operating environment\.
+operating environment.
.sp
.LP
The values returned by the driver in the \fBDL_INFO_ACK\fR response are:
@@ -52,60 +49,58 @@ The values returned by the driver in the \fBDL_INFO_ACK\fR response are:
.TP
.ie t \(bu
.el o
-Maximum SDU is 1500\.
+Maximum SDU is 1500.
.RE
.RS +4
.TP
.ie t \(bu
.el o
-Minimum SDU is 0\.
+Minimum SDU is 0.
.RE
.RS +4
.TP
.ie t \(bu
.el o
-The dlsap address length is 8\.
+The dlsap address length is 8.
.RE
.RS +4
.TP
.ie t \(bu
.el o
-MAC type is \fBDL_ETHER\fR\.
+MAC type is \fBDL_ETHER\fR.
.RE
.RS +4
.TP
.ie t \(bu
.el o
-SAP length is \-2\. The 6\-byte physical address is immediately followed by a
-2\-byte SAP\.
+SAP length is \-2. The 6\-byte physical address is immediately followed by a
+2\-byte SAP.
.RE
.RS +4
.TP
.ie t \(bu
.el o
-Service mode is \fBDL_CLDLS\fR\.
+Service mode is \fBDL_CLDLS\fR.
.RE
.RS +4
.TP
.ie t \(bu
.el o
The broadcast address is the 6\-byte Ethernet broadcast address
-(\fBff:ff:ff:ff:ff:ff\fR)\.
+(\fBff:ff:ff:ff:ff:ff\fR).
.SH "CONFIGURATION"
-.sp
-.LP
The \fBpcn\fR driver performs auto-negotiation to select the link speed and
-mode\. Link sped may be 100Mbps full\-duplex, 100Mbps half\-duplex,
+mode. Link speed may be 100Mbps full\-duplex, 100Mbps half\-duplex,
10Mbps full\-duplex, or 10Mbps half\-duplex, depending on the hardware
-adapter type\. See the \fIIEEE802.3\fR standard for more information\.
+adapter type. See the \fIIEEE802.3\fR standard for more information.
.sp
.LP
The capabilities advertised by the \fBpcn\fR device can be set using
-\fBdladm\fR(1m)\. The driver supports a number of parameters whose names
-being with \fBen_\fR (see below)\. Each of these parameters contains a
-boolean value that determines if the devices advertises that mode of
-operations\. The \fBadv_autoneg_cap\fR parameter controls whether
-auto-negotioation is performed\. If \fBadv_autoneg_cap\fR is set to 0, the
+\fBdladm\fR(1m). The driver supports a number of parameters whose names
+begin with \fBen_\fR (see below). Each of these parameters contains a
+boolean value that determines if the device advertises that mode of
+operations. The \fBadv_autoneg_cap\fR parameter controls whether
+auto-negotiation is performed. If \fBadv_autoneg_cap\fR is set to 0, the
driver forces the mode of operation selected by the first non-zero
parameter in priority order as shown below:
.sp
@@ -120,12 +115,10 @@ parameter in priority order as shown below:
.sp
.LP
-All capabilities default to enabled\. Note that changing any capability
-parameter causes te link to go down while the link partners renegotiate
-the link speed/duplex using the newly changed capabilities\.
+All capabilities default to enabled. Note that changing any capability
+parameter causes the link to go down while the link partners renegotiate
+the link speed/duplex using the newly changed capabilities.
.SH "ATTRIBUTES"
-.sp
-.LP
See \fBattributes\fR(5) for a description of the following attributes:
.sp
@@ -142,39 +135,26 @@ Interface Stability Committed
.TE
.SH "FILES"
-.sp
.ne 2
.na
-\fB\fB/dev/pcn\fR\fR
+\fB/dev/pcn\fR
.ad
.sp .6
.RS 4n
-Special character device\.
+Special character device.
.RE
.sp
.ne 2
.na
-\fB\fB/kernel/drv/pcn\fR\fR
-.ad
-.sp 6
-.RS 4n
-32\-bit driver binary\.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fB/kernel/drv/amd64/pcn\fR\fR
+\fB/kernel/drv/amd64/pcn\fR
.ad
.sp .6
.RS 4n
-64\-bit driver binary (x86)\.
+Device driver (x86)
.RE
.SH "SEE ALSO"
-.sp
-.LP
\fBattributes\fR(5), \fBstreamio\fR(7I), \fBdlpi\fR(7p)
.sp
.LP
diff --git a/usr/src/man/man7d/poll.7d b/usr/src/man/man7d/poll.7d
index 450da9ec5b..b69594d7da 100644
--- a/usr/src/man/man7d/poll.7d
+++ b/usr/src/man/man7d/poll.7d
@@ -3,11 +3,10 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH POLL 7D "April 9, 2016"
+.TH POLL 7D "January 10, 2020"
.SH NAME
poll \- driver for fast poll on many file descriptors
.SH SYNOPSIS
-.LP
.nf
\fB#include <sys/devpoll.h>
int fd = open("/dev/poll", O_RDWR);
@@ -71,7 +70,6 @@ Pointer to \fBpollfd\fR structure.
.RE
.SH DESCRIPTION
-.LP
The \fB/dev/poll\fR driver is a special driver that enables you to monitor
multiple sets of polled file descriptors. By using the \fB/dev/poll\fR
driver, you can efficiently poll large numbers of file descriptors. Access to
@@ -140,7 +138,7 @@ value \fB0\fR means the ioctl is timed out. In this case, the memory content
pointed by \fBdp_fds\fR is not modified. If the call is successful, it returns
the number of valid \fBpollfd\fR entries in the array pointed by \fBdp_fds\fR;
the contents of the rest of the buffer is undefined. For each valid
-\fBpollfd\fR entry, the \fBfd\fR field indicates the file desciptor on which
+\fBpollfd\fR entry, the \fBfd\fR field indicates the file descriptor on which
the polled \fBevents\fR happened. The \fBevents\fR field is the user specified
\fBpoll\fR \fBevents\fR. The \fBrevents\fR field contains the \fBevents\fR
occurred. \fB-1\fR is returned if the call fails.
@@ -155,7 +153,6 @@ currently polled \fBevents\fR. The ioctl returns \fB0\fR if the file
descriptor is not in the set. The \fBpollfd\fR structure pointed by \fIpfd\fR
is not modified. The ioctl returns a \fB-1\fR if the call fails.
.SH EXAMPLES
-.LP
The following example shows how \fB/dev/poll\fR may be used.
.sp
.in +2
@@ -335,7 +332,6 @@ special file does not exist.
.RE
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for a description of the following attributes:
.sp
@@ -351,10 +347,8 @@ MT-Level Safe
.TE
.SH SEE ALSO
-.LP
\fBopen\fR(2), \fBpoll\fR(2), \fBwrite\fR(2), \fBattributes\fR(5)
.SH NOTES
-.LP
The \fB/dev/poll\fR API is particularly beneficial to applications that poll a
large number of file descriptors repeatedly. Applications will exhibit the
best performance gain if the polled file descriptor list rarely change.
diff --git a/usr/src/man/man7d/qede.7d b/usr/src/man/man7d/qede.7d
index 28472dbf8f..b819e21697 100644
--- a/usr/src/man/man7d/qede.7d
+++ b/usr/src/man/man7d/qede.7d
@@ -11,7 +11,7 @@
.\"
.\" Copyright (c) 2014 QLogic Corporation. All Rights Reserved
.\"
-.Dd August 28, 2017
+.Dd January 10, 2020
.Dt QEDE 7D
.Os
.Sh NAME
@@ -89,7 +89,7 @@ for Tx ring stats
for general driver stats and version info.
.El
.Pp
-To get a list of all the individual statistics in these goups run:
+To get a list of all the individual statistics in these groups run:
.Bd -literal -offset indent
# kstat -m qede -i 0 -l
.Ed
diff --git a/usr/src/man/man7d/sd.7d b/usr/src/man/man7d/sd.7d
index 476a048757..cefbde57e7 100644
--- a/usr/src/man/man7d/sd.7d
+++ b/usr/src/man/man7d/sd.7d
@@ -3,24 +3,21 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER,
.\" with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH SD 7D "May 13, 2017"
+.TH SD 7D "Jan 10, 2020"
.SH NAME
sd \- SCSI disk and ATAPI/SCSI CD-ROM device driver
.SH SYNOPSIS
-.LP
.nf
\fBsd@target,lun:partition\fR
.fi
.SH DESCRIPTION
-.LP
To open a device without checking if the vtoc is valid, use the O_NDELAY flag.
When the device is opened using O_NDELAY, the first read or write to the device
that happens after the open results in the label being read if the label is not
currently valid. Once read, the label remains valid until the last close of the
device. Except for reading the label, O_NDELAY has no impact on the driver.
.SS "SPARC"
-.LP
The \fBsd\fR \fBSCSI\fR and \fBSCSI/ATAPI\fR driver supports embedded
\fBSCSI\fR-2 and \fBCCS\fR-compatible \fBSCSI\fR disk and CD-ROM drives,
\fBATAPI \fR 2.6 (SFF-8020i)-compliant CD-ROM drives, SFF-8090-compliant
@@ -34,8 +31,7 @@ describes the disk geometry and partitioning and must be present for the disk
to be mounted by the system.) A volume label is not required for removable,
re-writable or read-only media.
.SS "x86 Only"
-.LP
-The \fBsd\fRdriver supports embedded \fBSCSI\fR-2 and \fBCCS\fR-compatible
+The \fBsd\fR driver supports embedded \fBSCSI\fR-2 and \fBCCS\fR-compatible
\fBSCSI \fRdisk and CD-ROM drives, \fBATAPI \fR2.6 (SFF-8020i)-compliant CD-ROM
drives, SFF-8090-compliant \fBSCSI/ATAPI\fR DVD-ROM drives, IOMEGA
\fBSCSI/ATAPI\fR ZIP drives\fB, and SCSI JAZ\fR drives.
@@ -46,7 +42,6 @@ in the first physical sector of the bootable media. If the x86 hard disk
contains a Solaris disk label, it is located in the second 512-byte sector of
the FDISK partition.
.SH DEVICE SPECIAL FILES
-.LP
Block-files access the disk using normal buffering mechanism and are read-from
and written-to without regard to physical disk records. A \fBraw\fR interface
enables direct transmission between the disk and the user's read or write
@@ -62,7 +57,6 @@ Requests that do not meet these requirements will trigger an \fBEINVAL\fR
error. There are no alignment or length restrictions on I/O requests to the
block device.
.SH CD-ROM DRIVE SUPPORT
-.LP
A CD-ROM disk is single-sided and contains approximately 640 megabytes of data
or 74 minutes of audio. When the CD-ROM is opened, the eject button is disabled
to prevent manual removal of the disk until the last \fBclose()\fR is called.
@@ -71,7 +65,6 @@ information are constant and never change. If the CD-ROM contains data recorded
in a Solaris-aware file system format, it can be mounted using the appropriate
Solaris file system support.
.SH DVD-ROM DRIVE SUPPORT
-.LP
DVD-ROM media can be single or double-sided and can be recorded upon using a
single or double layer structure. Double-layer media provides parallel or
opposite track paths. A DVD-ROM can hold from between 4.5 Gbytes and 17 Gbytes
@@ -85,7 +78,6 @@ required for a DVD-ROM. If the DVD-ROM contains data recorded in a
Solaris-aware file system format, it can be mounted using the appropriate
Solaris file system support.
.SH ZIP/JAZ DRIVE SUPPORT
-.LP
\fBZIP/JAZ\fR media provide varied data capacity points; a single \fBJAZ
\fRdrive can store up to 2 GBytes of data, while a ZIP-250 can store up to
250MBytes of data. \fBZIP/JAZ\fR drives can be read-from or written-to using
@@ -98,7 +90,6 @@ label is required for a \fBZIP/JAZ\fR drive. If the \fBZIP/JAZ\fR drive
contains data recorded in a Solaris-aware file system format, it can be mounted
using the appropriate Solaris file system support.
.SH DEVICE STATISTICS SUPPORT
-.LP
Each device maintains I/O statistics for the device and for partitions
allocated for that device. For each device/partition, the driver accumulates
reads, writes, bytes read, and bytes written. The driver also initiates
@@ -108,9 +99,8 @@ of residence time and cumulative residence-length product for each queue.
.LP
Not all device drivers make per-partition IO statistics available for
reporting. \fBsd\fR and \fBssd\fR(7D) per-partition statistics are enabled by
-default but may disabled in their configuration files.
+default but may be disabled in their configuration files.
.SH IOCTLS
-.LP
Refer to \fBdkio\fR(7I), and \fBcdio\fR(7I)
.SS "ERRORS"
.ne 2
@@ -208,7 +198,7 @@ Insufficient memory
\fB\fBEPERM\fR\fR
.ad
.RS 10n
-Insufficent access permission
+Insufficient access permission
.RE
.sp
@@ -222,7 +212,6 @@ media.
.RE
.SH CONFIGURATION
-.LP
The \fBsd\fR driver can be configured by defining properties in the
\fBsd.conf\fR file. The \fBsd\fR driver supports the following properties:
.sp
@@ -448,7 +437,6 @@ Where \fIn\fR=0 the node corresponds to the entire disk.
.RE
.SH SEE ALSO
-.LP
\fBsar\fR(1), \fBcfgadm_scsi\fR(1M), \fBfdisk\fR(1M), \fBformat\fR(1M),
\fBiostat\fR(1M), \fBclose\fR(2), \fBioctl\fR(2), \fBlseek\fR(2),
\fBread\fR(2), \fBwrite\fR(2), \fBdriver.conf\fR(4), \fBscsi\fR(4),
@@ -845,7 +833,6 @@ Drive went offline; probably powered down.
.RE
.SH NOTES
-.LP
DVD-ROM media containing DVD-Video data may follow/adhere to the requirements
of content scrambling system or copy protection scheme. Reading of
copy-protected sector will cause I/O error. Users are advised to use the
diff --git a/usr/src/man/man7d/usba.7d b/usr/src/man/man7d/usba.7d
index 3b87e86dff..25cc63c8ea 100644
--- a/usr/src/man/man7d/usba.7d
+++ b/usr/src/man/man7d/usba.7d
@@ -1,9 +1,10 @@
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
.\" Copyright 2016 Joyent, Inc.
+.\" Copyright 2020 Peter Tribble.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.Dd May 13, 2017
+.Dd Jan 10, 2020
.Dt USBA 7D
.Os
.Sh NAME
@@ -38,51 +39,39 @@ Devices without a driver may be able to leverage libusb.
.Sh FILES
Listed below are drivers and modules which either utilize or are utilized by
USBA.
-Drivers in
-.Pa /kernel/drv
-are 32 bit drivers (x86 only).
-Drivers in
-.Pa /kernel/drv/sparcv9
-or
-.Pa kernel/drv/amd64
-are 64 bit drivers.
-.Bl -column -offset indent ".Pa kernel/drv/[sparcv9|amd64/]usbser_edge" "Edgeport USB to serial port"
+.Bl -column -offset indent ".Pa /kernel/drv/[sparcv9|amd64]/usbser_edge" "Edgeport USB to serial port"
.It Em Client Driver Ta Em Function/Device
.It Ta
-.It Pa kernel/drv/[sparcv9|amd64/]hid Ta HID class
-.It Pa kernel/drv/[sparcv9|amd64/]hubd Ta hub class
-.It Pa kernel/drv/[sparcv9|amd64/]scsa2usb Ta mass storage class
-.It Pa kernel/drv/[sparcv9|amd64/]usbprn Ta printer class
-.It Pa kernel/drv/[sparcv9|amd64/]usb_as Ta audio streaming class
-.It Pa kernel/drv/[sparcv9|amd64/]usb_ac Ta audio control class
-.It Pa kernel/drv/[sparcv9|amd64/]usbvc Ta video class
-.It Pa kernel/drv/[sparcv9|amd64/]usb_mid Ta multi-interface device
-.It Pa kernel/drv/[sparcv9|amd64/]usb_ia Ta interface-association driver
-.It Pa kernel/drv/[sparcv9|amd64/]usbser_edge Ta Edgeport USB to serial port
-.It Pa kernel/drv/[sparcv9|amd64/]usbsksp Ta Keyspan USB to serial port
-.It Pa kernel/drv/[sparcv9|amd64/]usbsprl Ta pl2303 USB to serial port
-.It Pa kernel/drv/[sparcv9|amd64/]usbsacm Ta CDC ACM class to serial port
-.It Pa kernel/drv/[sparcv9|amd64/]ugen Ta generic USB driver
-.It Pa kernel/drv/[sparcv9|amd64/]ohci Ta open host controller driver
-.It Pa kernel/drv/[sparcv9|amd64/]uhci Ta universal host controller driver
-.It Pa kernel/drv/[sparcv9|amd64/]ehci Ta enhanced host controller driver
-.It Pa kernel/drv/[sparcv9|amd64/]xhci Ta extensible host controller driver
+.It Pa /kernel/drv/[sparcv9|amd64]/hid Ta HID class
+.It Pa /kernel/drv/[sparcv9|amd64]/hubd Ta hub class
+.It Pa /kernel/drv/[sparcv9|amd64]/scsa2usb Ta mass storage class
+.It Pa /kernel/drv/[sparcv9|amd64]/usbprn Ta printer class
+.It Pa /kernel/drv/[sparcv9|amd64]/usb_as Ta audio streaming class
+.It Pa /kernel/drv/[sparcv9|amd64]/usb_ac Ta audio control class
+.It Pa /kernel/drv/[sparcv9|amd64]/usbvc Ta video class
+.It Pa /kernel/drv/[sparcv9|amd64]/usb_mid Ta multi-interface device
+.It Pa /kernel/drv/[sparcv9|amd64]/usb_ia Ta interface-association driver
+.It Pa /kernel/drv/[sparcv9|amd64]/usbser_edge Ta Edgeport USB to serial port
+.It Pa /kernel/drv/[sparcv9|amd64]/usbsksp Ta Keyspan USB to serial port
+.It Pa /kernel/drv/[sparcv9|amd64]/usbsprl Ta pl2303 USB to serial port
+.It Pa /kernel/drv/[sparcv9|amd64]/usbsacm Ta CDC ACM class to serial port
+.It Pa /kernel/drv/[sparcv9|amd64]/ugen Ta generic USB driver
.El
.Bl -column -offset indent ".Pa /kernel/strmod/[sparcv9|amd64/]usb_ah" "Function/Device"
.It Ta
.It Em Client Streams Modules Ta Em Function/Device
.It Ta
-.It Pa /kernel/strmod/[sparcv9|amd64/]usbkbm Ta Keyboad
-.It Pa /kernel/strmod/[sparcv9|amd64/]usbms Ta Mouse
-.It Pa /kernel/strmod/[sparcv9|amd64/]usb_ah Ta Audio HID
+.It Pa /kernel/strmod/[sparcv9|amd64]/usbkbm Ta Keyboard
+.It Pa /kernel/strmod/[sparcv9|amd64]/usbms Ta Mouse
+.It Pa /kernel/strmod/[sparcv9|amd64]/usb_ah Ta Audio HID
.El
.Bl -column -offset indent ".Em Host Controller Interface Drivers" "Extensible HCI"
.It Em Host Controller Interface Drivers Ta Em Device
.It Ta
-.It Pa /kernel/drv/[amd64/]xhci Ta Extensible HCI
-.It Pa /kernel/drv/[sparcv9|amd64/]ehci Ta Enhanced HCI
-.It Pa /kernel/drv/[sparcv9|amd64/]ohci Ta Open HCI
-.It Pa /kernel/drv/[sparcv|amd64/]uhci Ta Universal HCI
+.It Pa /kernel/drv/amd64/xhci Ta Extensible HCI
+.It Pa /kernel/drv/[sparcv9|amd64]/ehci Ta Enhanced HCI
+.It Pa /kernel/drv/[sparcv9|amd64]/ohci Ta Open HCI
+.It Pa /kernel/drv/[sparcv9|amd64]/uhci Ta Universal HCI
.El
.Sh DIAGNOSTICS
The messages described below may appear on the system console as well as being
@@ -110,7 +99,7 @@ They are formatted in the following manner:
<device path><usba<instance number>): message...
.Ed
.Bl -tag -width Sy -offset 2n
-.It Sy Incorrect USB driver version for <n.m>.
+.It Sy Incorrect USB driver version for <n.m> .
Driver is incompatible with USBA framework.
.El
.Sh SEE ALSO
diff --git a/usr/src/man/man7d/xhci.7d b/usr/src/man/man7d/xhci.7d
index 4f2836b97e..a915a099b1 100644
--- a/usr/src/man/man7d/xhci.7d
+++ b/usr/src/man/man7d/xhci.7d
@@ -11,7 +11,7 @@
.\"
.\" Copyright 2016 Joyent, Inc.
.\"
-.Dd October 17, 2016
+.Dd January 10, 2020
.Dt XHCI 7D
.Os
.Sh NAME
@@ -23,7 +23,7 @@
The
.Nm
driver supports PCI devices that implement versions 1.0 and 1.1 of the
-Extensible Host Controller Inteface Specification.
+Extensible Host Controller Interface Specification.
These devices provide support for USB 3.0, USB 2.x, and USB 1.x devices and is
integrated into the broader illumos USB Architecture (USBA).
.Pp
@@ -94,12 +94,10 @@ driver is only supported on
systems at this time.
.Sh FILES
.Bl -tag -width Pa
-.It Pa /kernel/drv/xhci
-32-bit device driver (x86).
.It Pa /kernel/drv/amd64/xhci
-64-bit device driver (x86).
+Device driver (x86)
.It Pa /kernel/drv/xhci.conf
-Driver configuration file.
+Driver configuration file
.El
.Sh SEE ALSO
.Xr cfgadm 1M ,
diff --git a/usr/src/man/man7i/Makefile b/usr/src/man/man7i/Makefile
index 12e4489be5..9d5267ddb2 100644
--- a/usr/src/man/man7i/Makefile
+++ b/usr/src/man/man7i/Makefile
@@ -16,7 +16,7 @@
include $(SRC)/Makefile.master
-MANSECT= 7i
+MANSECT= 7i
MANFILES= audio.7i \
cdio.7i \
@@ -41,6 +41,10 @@ MANFILES= audio.7i \
visual_io.7i \
vt.7i
+MANLINKS= uccid.7i
+
+uccid.7i := LINKSRC = ../man7d/ccid.7d
+
.KEEP_STATE:
include $(SRC)/man/Makefile.man
diff --git a/usr/src/pkg/manifests/SUNWcs.mf b/usr/src/pkg/manifests/SUNWcs.mf
index 967f1a18b6..35a3665e6d 100644
--- a/usr/src/pkg/manifests/SUNWcs.mf
+++ b/usr/src/pkg/manifests/SUNWcs.mf
@@ -831,6 +831,7 @@ file path=usr/bin/setfacl mode=0555
file path=usr/bin/setpgrp group=sys mode=0555
file path=usr/bin/settime mode=0555
file path=usr/bin/shcomp mode=0555
+file path=usr/bin/sleep mode=0555
file path=usr/bin/strchg group=root mode=0555
file path=usr/bin/strconf group=root mode=0555
file path=usr/bin/stty mode=0555
@@ -1474,7 +1475,6 @@ hardlink path=usr/bin/rksh target=../../usr/lib/isaexec
hardlink path=usr/bin/rksh93 target=../../usr/lib/isaexec
hardlink path=usr/bin/savecore target=../../usr/lib/isaexec
hardlink path=usr/bin/setuname target=../../usr/lib/isaexec
-hardlink path=usr/bin/sleep target=../../usr/bin/alias
hardlink path=usr/bin/sum target=../../usr/bin/alias
hardlink path=usr/bin/tee target=../../usr/bin/alias
hardlink path=usr/bin/test target=../../usr/bin/alias
diff --git a/usr/src/pkg/manifests/system-test-utiltest.mf b/usr/src/pkg/manifests/system-test-utiltest.mf
index 4e7ab3a497..ff45bf45f2 100644
--- a/usr/src/pkg/manifests/system-test-utiltest.mf
+++ b/usr/src/pkg/manifests/system-test-utiltest.mf
@@ -68,6 +68,7 @@ dir path=opt/util-tests/tests/awk/gnu
dir path=opt/util-tests/tests/awk/syn
dir path=opt/util-tests/tests/awk/tests
dir path=opt/util-tests/tests/mergeq
+dir path=opt/util-tests/tests/sleep
file path=opt/util-tests/README mode=0444
file path=opt/util-tests/bin/btest mode=0555
file path=opt/util-tests/bin/print_json mode=0555
@@ -1479,6 +1480,9 @@ file path=opt/util-tests/tests/mergeq/wqt mode=0555
file path=opt/util-tests/tests/printf_test mode=0555
file path=opt/util-tests/tests/set-linkprop mode=0555
file path=opt/util-tests/tests/show-overlay-exit mode=0555
+file path=opt/util-tests/tests/sleep/sleep.awk mode=0444
+file path=opt/util-tests/tests/sleep/sleep.d mode=0444
+file path=opt/util-tests/tests/sleep/sleeptest mode=0555
file path=opt/util-tests/tests/smbios mode=0555
file path=opt/util-tests/tests/vnic-mtu mode=0555
file path=opt/util-tests/tests/xargs_test mode=0555
@@ -1487,5 +1491,6 @@ license usr/src/lib/libdemangle/THIRDPARTYLICENSE \
license=usr/src/lib/libdemangle/THIRDPARTYLICENSE
license usr/src/test/util-tests/tests/demangle/THIRDPARTYLICENSE.rust \
license=usr/src/test/util-tests/tests/demangle/THIRDPARTYLICENSE.rust
+depend fmri=locale/de type=require
depend fmri=system/library/iconv/utf-8 type=require
depend fmri=system/test/testrunner type=require
diff --git a/usr/src/prototypes/README b/usr/src/prototypes/README
index 9caa807985..85dfe77bcd 100644
--- a/usr/src/prototypes/README
+++ b/usr/src/prototypes/README
@@ -17,5 +17,5 @@ CDDL version 1.0 for each new file introduced in illumos.
*/
/*
- * Copyright 2019 <contributor>
+ * Copyright 2020 <contributor>
*/
diff --git a/usr/src/prototypes/prototype.Makefile b/usr/src/prototypes/prototype.Makefile
index 1076b28511..76e4c9f402 100644
--- a/usr/src/prototypes/prototype.Makefile
+++ b/usr/src/prototypes/prototype.Makefile
@@ -10,6 +10,6 @@
#
#
-# Copyright 2019 <contributor>
+# Copyright 2020 <contributor>
#
diff --git a/usr/src/prototypes/prototype.c b/usr/src/prototypes/prototype.c
index c5b0610800..697ca2f594 100644
--- a/usr/src/prototypes/prototype.c
+++ b/usr/src/prototypes/prototype.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2019 <contributor>
+ * Copyright 2020 <contributor>
*/
/*
diff --git a/usr/src/prototypes/prototype.csh b/usr/src/prototypes/prototype.csh
index 8a0b329c85..60fa7f3e8f 100644
--- a/usr/src/prototypes/prototype.csh
+++ b/usr/src/prototypes/prototype.csh
@@ -12,6 +12,6 @@
#
#
-# Copyright 2019 <contributor>
+# Copyright 2020 <contributor>
#
diff --git a/usr/src/prototypes/prototype.h b/usr/src/prototypes/prototype.h
index ad13c8d3bc..b92e6aae09 100644
--- a/usr/src/prototypes/prototype.h
+++ b/usr/src/prototypes/prototype.h
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2019 <contributor>
+ * Copyright 2020 <contributor>
*/
#ifndef _PROTOTYPE_H
diff --git a/usr/src/prototypes/prototype.java b/usr/src/prototypes/prototype.java
index fb1244599a..4de4a78a94 100644
--- a/usr/src/prototypes/prototype.java
+++ b/usr/src/prototypes/prototype.java
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2019 <contributor>
+ * Copyright 2020 <contributor>
*/
/*
diff --git a/usr/src/prototypes/prototype.ksh b/usr/src/prototypes/prototype.ksh
index 461f955820..f69153663f 100644
--- a/usr/src/prototypes/prototype.ksh
+++ b/usr/src/prototypes/prototype.ksh
@@ -12,6 +12,6 @@
#
#
-# Copyright 2019 <contributor>
+# Copyright 2020 <contributor>
#
diff --git a/usr/src/prototypes/prototype.man b/usr/src/prototypes/prototype.man
index f611dcfa6b..4c5cc3e514 100644
--- a/usr/src/prototypes/prototype.man
+++ b/usr/src/prototypes/prototype.man
@@ -9,5 +9,5 @@
.\" http://www.illumos.org/license/CDDL.
.\"
.\"
-.\" Copyright 2019 <contributor>
+.\" Copyright 2020 <contributor>
.\"
diff --git a/usr/src/prototypes/prototype.mapfile-vers b/usr/src/prototypes/prototype.mapfile-vers
index 3a97d1dffa..08463224c1 100644
--- a/usr/src/prototypes/prototype.mapfile-vers
+++ b/usr/src/prototypes/prototype.mapfile-vers
@@ -10,7 +10,7 @@
#
#
-# Copyright 2019 <contributor>
+# Copyright 2020 <contributor>
#
#
diff --git a/usr/src/prototypes/prototype.pl b/usr/src/prototypes/prototype.pl
index e010953597..459d961062 100644
--- a/usr/src/prototypes/prototype.pl
+++ b/usr/src/prototypes/prototype.pl
@@ -11,7 +11,7 @@
#
#
-# Copyright 2019 <contributor>
+# Copyright 2020 <contributor>
#
#
diff --git a/usr/src/prototypes/prototype.py b/usr/src/prototypes/prototype.py
index fcaf1e3fe2..d6240365a5 100644
--- a/usr/src/prototypes/prototype.py
+++ b/usr/src/prototypes/prototype.py
@@ -11,7 +11,7 @@
#
#
-# Copyright 2019 <contributor>
+# Copyright 2020 <contributor>
#
#
diff --git a/usr/src/prototypes/prototype.s b/usr/src/prototypes/prototype.s
index 00009214db..91ca220507 100644
--- a/usr/src/prototypes/prototype.s
+++ b/usr/src/prototypes/prototype.s
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2019 <contributor>
+ * Copyright 2020 <contributor>
*/
.file "prototype.s"
diff --git a/usr/src/prototypes/prototype.sh b/usr/src/prototypes/prototype.sh
index e048830fb2..fae70c599c 100644
--- a/usr/src/prototypes/prototype.sh
+++ b/usr/src/prototypes/prototype.sh
@@ -12,6 +12,6 @@
#
#
-# Copyright 2019 <contributor>
+# Copyright 2020 <contributor>
#
diff --git a/usr/src/test/os-tests/runfiles/default.run b/usr/src/test/os-tests/runfiles/default.run
index 9489c2faf6..940b9bf97a 100644
--- a/usr/src/test/os-tests/runfiles/default.run
+++ b/usr/src/test/os-tests/runfiles/default.run
@@ -87,3 +87,11 @@ tests = ['ldt', 'badseg']
[/opt/os-tests/tests/imc_test]
arch = i86pc
+
+#
+# Except atrparse all tests require special hardware (CCID YubiKey) to run,
+# hence they aren't included in the default runfile.
+#
+[/opt/os-tests/tests/uccid]
+arch = i86pc
+tests = ['atrparse']
diff --git a/usr/src/test/os-tests/tests/Makefile b/usr/src/test/os-tests/tests/Makefile
index e3e756c946..3107af540c 100644
--- a/usr/src/test/os-tests/tests/Makefile
+++ b/usr/src/test/os-tests/tests/Makefile
@@ -29,6 +29,7 @@ SUBDIRS = \
stress \
timer \
tmpfs \
+ uccid \
$(SUBDIRS_$(MACH))
PROGS = \
diff --git a/usr/src/test/os-tests/tests/uccid/Makefile b/usr/src/test/os-tests/tests/uccid/Makefile
new file mode 100644
index 0000000000..55fd96bfd5
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/Makefile
@@ -0,0 +1,82 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/os-tests
+TESTDIR = $(ROOTOPTPKG)/tests/uccid
+
+PROGS = \
+ atrparse \
+ excl-basic \
+ excl-badread \
+ excl-close \
+ excl-loop \
+ excl-nonblock \
+ excl-reset \
+ modify \
+ notxn-poll \
+ status \
+ pollin \
+ pollout \
+ txn-pollerr \
+ yk \
+ yk-poll \
+ yk-readonly
+
+COMMON_OBJS = \
+ atr.o
+
+atrparse := EXTRA_OBJS = $(COMMON_OBJS)
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+CMDS = $(PROGS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+CPPFLAGS += -D_REENTRANT -I$(SRC)/common/ccid/
+
+all: $(PROGS)
+
+install: all $(CMDS)
+
+clobber: clean
+ -$(RM) $(PROGS)
+
+clean:
+ -$(RM) *.o
+
+$(PROGS): $(COMMON_OBJS)
+
+$(CMDS): $(TESTDIR) $(PROGS)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
+
+%.o: $(SRC)/common/ccid/%.c
+ $(COMPILE.c) -o $@ -c $<
+ $(POST_PROCESS_O)
+
+%.o: %.c
+ $(COMPILE.c) -o $@ -c $<
+ $(POST_PROCESS_O)
+
+%: %.o
+ $(LINK.c) -o $@ $< $(EXTRA_OBJS)
+ $(POST_PROCESS)
diff --git a/usr/src/test/os-tests/tests/uccid/atrparse.c b/usr/src/test/os-tests/tests/uccid/atrparse.c
new file mode 100644
index 0000000000..f14bbfd34d
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/atrparse.c
@@ -0,0 +1,731 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can parse various forms of ATR data and detect invalid data.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+
+#include <atr.h>
+
+typedef struct atr_vals {
+} atr_vals_t;
+
+typedef struct atr_test {
+ const char *ar_test;
+ uint8_t ar_len;
+ uint8_t ar_buf[64];
+ atr_parsecode_t ar_retval;
+ /* Everything after this is data from the ATR */
+ atr_protocol_t ar_sup;
+ atr_protocol_t ar_def;
+ boolean_t ar_neg;
+ uint8_t ar_fi;
+ uint8_t ar_di;
+ atr_convention_t ar_conv;
+ uint8_t ar_guard;
+ atr_clock_stop_t ar_stop;
+ /* These will be checked based on sup prot */
+ uint8_t ar_t0_wi;
+ atr_t1_checksum_t ar_t1_cksum;
+ uint8_t ar_t1_bwi;
+ uint8_t ar_t1_cwi;
+ uint8_t ar_t1_ifsc;
+} atr_test_t;
+
+atr_test_t atr_tests[] = {
+ { "zero-length data", 0, { 0 }, ATR_CODE_TOO_SHORT },
+ { "No T0", 1, { 0x3f }, ATR_CODE_TOO_SHORT },
+ { "Too much data", 34, { 0 }, ATR_CODE_TOO_LONG },
+ { "Overrun T0 (1)", 2, { 0x3b, 0x10 }, ATR_CODE_OVERRUN },
+ { "Overrun T0 (2)", 2, { 0x3b, 0x80 }, ATR_CODE_OVERRUN },
+ { "Overrun T0 (3)", 2, { 0x3b, 0x01 }, ATR_CODE_OVERRUN },
+ { "Overrun T0 (4)", 2, { 0x3b, 0x11 }, ATR_CODE_OVERRUN },
+ { "Overrun T0 (5)", 2, { 0x3b, 0xff }, ATR_CODE_OVERRUN },
+ { "Overrun TD1", 3, { 0x3b, 0x80, 0x10 }, ATR_CODE_OVERRUN },
+ { "Overrun TD2", 4, { 0x3b, 0x80, 0x80, 0x10 }, ATR_CODE_OVERRUN },
+ { "Overrun TD", 33, { 0x3b, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80 }, ATR_CODE_OVERRUN },
+ { "T0 w/ T=15 and no cksum", 5, { 0x3b, 0x80, 0x80, 0x1f, 0x00 },
+ ATR_CODE_OVERRUN },
+ { "Bad TS (1)", 2, { 0x3a, 0x00 }, ATR_CODE_INVALID_TS },
+ { "Bad TS (2)", 2, { 0xff, 0x00 }, ATR_CODE_INVALID_TS },
+ { "T0 w/ T=15 and bad cksum", 6, { 0x3b, 0x80, 0x80, 0x1f, 0x00, 0x00 },
+ ATR_CODE_CHECKSUM_ERROR },
+ { "T0 w/ T=15 and bad cksum (make sure no TS)", 6,
+ { 0x3b, 0x80, 0x80, 0x1f, 0x00, 0x24 },
+ ATR_CODE_CHECKSUM_ERROR },
+ { "T=15 in TD1", 4, { 0x3b, 0x80, 0x0f, 0x8f }, ATR_CODE_INVALID_TD1 },
+ {
+ .ar_test = "Minimal T0 Direct",
+ .ar_len = 2,
+ .ar_buf = { 0x3b, 0x00 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "Minimal T0 Inverse",
+ .ar_len = 2,
+ .ar_buf = { 0x3f, 0x00 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_INVERSE,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Fi/Di (1)",
+ .ar_len = 3,
+ .ar_buf = { 0x3b, 0x10, 0x24 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 2,
+ .ar_di = 4,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Fi/Di (2)",
+ .ar_len = 3,
+ .ar_buf = { 0x3b, 0x10, 0x93 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 9,
+ .ar_di = 3,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Ignore deprecated TB1",
+ .ar_len = 3,
+ .ar_buf = { 0x3b, 0x20, 0x42 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Ignore deprecated TB2",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x20, 0x42 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Ignore deprecated TB1/TB2",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0xa0, 0x55, 0x20, 0x42 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Encode TC1",
+ .ar_len = 3,
+ .ar_buf = { 0x3b, 0x40, 0x23 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0x23,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TA2 says neg",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x10, 0x00 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TA2 says not neg",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x10, 0x80 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_FALSE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TA2 says not neg, honor Fi/Di",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x90, 0x24, 0x10, 0x80 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_FALSE,
+ .ar_fi = 2,
+ .ar_di = 4,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TA2 says not neg, don't honor Fi/Di",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x90, 0x24, 0x10, 0x90 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_FALSE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TC2 set",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x40, 0x35 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 0x35,
+ }, {
+ .ar_test = "T0 T15 empty (requires checksum)",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x0f, 0x0f },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 T15 Clock Stop (1)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x07, 0x18 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 T15 Clock Stop (2)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x47, 0x58 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_LOW,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 T15 Clock Stop (3)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x87, 0x98 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_HI,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 T15 Clock Stop (4)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0xc7, 0xd8 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_BOTH,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 with random prots",
+ .ar_len = 7,
+ .ar_buf = { 0x3b, 0x80, 0x84, 0x85, 0x88, 0x0f, 0x06 },
+ .ar_sup = ATR_P_T0,
+ /*
+ * This comes from the fact that TD1 is T=4 and that isn't
+ * supported in the system.
+ */
+ .ar_def = ATR_P_NONE,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "Actual ATR (1, Yubikey4)",
+ .ar_len = 18,
+ .ar_buf = { 0x3b, 0xf8, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe,
+ 0x15, 0x59, 0x75, 0x62, 0x69, 0x6b, 0x65, 0x79, 0x34,
+ 0xd4 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 3,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 1,
+ .ar_t1_cwi = 5,
+ .ar_t1_ifsc = 254
+ }, {
+ .ar_test = "Actual ATR (2)",
+ .ar_len = 19,
+ .ar_buf = { 0x3b, 0xf9, 0x18, 0x00, 0x00, 0x81, 0x31, 0xfe,
+ 0x45, 0x4a, 0x32, 0x44, 0x30, 0x38, 0x31, 0x5f, 0x50, 0x56,
+ 0xb6 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 8,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 5,
+ .ar_t1_ifsc = 254
+ }, {
+ .ar_test = "Actual ATR (3)",
+ .ar_len = 22,
+ .ar_buf = { 0x3b, 0xfc, 0x18, 0x00, 0x00, 0x81, 0x31, 0x80,
+ 0x45, 0x90, 0x67, 0x46, 0x4a, 0x00, 0x64, 0x16, 0x6, 0xf2,
+ 0x72, 0x7e, 0x00, 0xe0 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 8,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 5,
+ .ar_t1_ifsc = 128
+ }, {
+ .ar_test = "Minimal T=1",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x01, 0x81 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1 Fi/Di",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x90, 0x34, 0x01, 0xa5 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 3,
+ .ar_di = 4,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1 TA2 says neg, T=1 def",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x80, 0x11, 0x11, 0x80 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=0, T=1 TA2 says neg, T=0 def",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x90, 0x10, 0x01, 0x01 },
+ .ar_sup = ATR_P_T0 | ATR_P_T1,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=0, T=1 TA2 says neg, T=1 def",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x90, 0x11, 0x01, 0x00 },
+ .ar_sup = ATR_P_T0 | ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=0, T=1 TA2 says not neg, T=0 def",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x90, 0x90, 0x01, 0x81 },
+ .ar_sup = ATR_P_T0 | ATR_P_T1,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_FALSE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=0, T=1 TA2 says not neg, T=1 def",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x90, 0x81, 0x01, 0x90 },
+ .ar_sup = ATR_P_T0 | ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_FALSE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1, BWI/CWI",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x81, 0x21, 0x59, 0x79 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 5,
+ .ar_t1_cwi = 9,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1, IFSC",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x81, 0x11, 0x49, 0x59 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 73
+ }, {
+ .ar_test = "T=1, Checksum (LRC)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x81, 0x41, 0x00, 0x40 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1, Checksum (CRC)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x81, 0x41, 0x01, 0x41 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_CRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }
+};
+
+static void
+atr_parse_failed(atr_test_t *test, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) fprintf(stderr, "Test \"%s\" failed: ", test->ar_test);
+ (void) vfprintf(stderr, fmt, ap);
+ (void) fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+static uint_t
+atr_parse_one(atr_data_t *data, atr_test_t *test)
+{
+ uint_t err = 0;
+ atr_parsecode_t ret;
+ atr_protocol_t sup, def;
+ boolean_t neg;
+ uint8_t fi, di, guard;
+ atr_convention_t conv;
+ atr_clock_stop_t stop;
+
+ ret = atr_parse(test->ar_buf, test->ar_len, data);
+ if (ret != test->ar_retval) {
+ atr_parse_failed(test, "found unexpected return "
+ "value: %u (%s), expected: %u", ret, atr_strerror(ret),
+ test->ar_retval);
+ return (1);
+ }
+
+ /* Don't test anything else if it's not OK */
+ if (ret != ATR_CODE_OK)
+ return (0);
+
+ sup = atr_supported_protocols(data);
+ def = atr_default_protocol(data);
+ neg = atr_params_negotiable(data);
+ fi = atr_fi_index(data);
+ di = atr_di_index(data);
+ conv = atr_convention(data);
+ guard = atr_extra_guardtime(data);
+ stop = atr_clock_stop(data);
+
+ if (sup != test->ar_sup) {
+ atr_parse_failed(test, "Found mismatched supported "
+ "protocols: %u, expected: %u", sup, test->ar_sup);
+ err++;
+ }
+
+ if (def != test->ar_def) {
+ atr_parse_failed(test, "Found mismatched default "
+ "protocols: %u, expected: %u", def, test->ar_def);
+ err++;
+ }
+
+ if (neg != test->ar_neg) {
+ atr_parse_failed(test, "Found mismatched negotiable bit: "
+ "%u, expected %u", neg, test->ar_neg);
+ err++;
+ }
+
+ if (fi != test->ar_fi) {
+ atr_parse_failed(test, "Found mismatched fi index: "
+ "%u, expected: %u", fi, test->ar_fi);
+ err++;
+ }
+
+ if (di != test->ar_di) {
+ atr_parse_failed(test, "Found mismatched di index: "
+ "%u, expected: %u", di, test->ar_di);
+ err++;
+ }
+
+ if (conv != test->ar_conv) {
+ atr_parse_failed(test, "Found mismatched TS convention: "
+ "%u, expected: %u", conv, test->ar_conv);
+ err++;
+ }
+
+ if (guard != test->ar_guard) {
+ atr_parse_failed(test, "Found mismatched extra guardtime: "
+ "%u, expected: %u", guard, test->ar_guard);
+ err++;
+ }
+
+ if (stop != test->ar_stop) {
+ atr_parse_failed(test, "Found mismatched clock stop: "
+ "%u, expected: %u", stop, test->ar_stop);
+ err++;
+ }
+
+ if ((sup & ATR_P_T0) != 0) {
+ uint8_t wi;
+
+ wi = atr_t0_wi(data);
+ if (wi != test->ar_t0_wi) {
+ atr_parse_failed(test, "Found mismatched T0 wi: "
+ "%u, expected: %u", wi, test->ar_t0_wi);
+ err++;
+ }
+ }
+
+ if ((sup & ATR_P_T1) != 0) {
+ atr_t1_checksum_t cksum;
+ uint8_t bwi, cwi, ifsc;
+
+ cksum = atr_t1_checksum(data);
+ bwi = atr_t1_bwi(data);
+ cwi = atr_t1_cwi(data);
+ ifsc = atr_t1_ifsc(data);
+
+ if (cksum != test->ar_t1_cksum) {
+ atr_parse_failed(test, "Found mistmatched T1 checksum: "
+ "%u, expected: %u", cksum, test->ar_t1_cksum);
+ err++;
+ }
+
+ if (bwi != test->ar_t1_bwi) {
+ atr_parse_failed(test, "Found mistmatched T1 bwi: "
+ "%u, expected: %u", bwi, test->ar_t1_bwi);
+ err++;
+ }
+
+ if (cwi != test->ar_t1_cwi) {
+ atr_parse_failed(test, "Found mistmatched T1 cwi: "
+ "%u, expected: %u", cwi, test->ar_t1_cwi);
+ err++;
+ }
+
+ if (ifsc != test->ar_t1_ifsc) {
+ atr_parse_failed(test, "Found mistmatched T1 ifsc: "
+ "%u, expected: %u", ifsc, test->ar_t1_ifsc);
+ err++;
+ }
+ }
+
+ if (err > 0) {
+ atr_data_dump(data, stderr);
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+main(void)
+{
+ uint_t i;
+ uint_t errs = 0;
+ atr_data_t *data;
+
+ data = atr_data_alloc();
+ if (data == NULL) {
+ errx(EXIT_FAILURE, "failed to allocate atr_data_t");
+ }
+
+ for (i = 0; i < sizeof (atr_tests) / sizeof (atr_test_t); i++) {
+ atr_data_reset(data);
+ errs += atr_parse_one(data, &atr_tests[i]);
+ }
+
+ atr_data_free(data);
+
+ if (errs != 0) {
+ warnx("%d test(s) failed", errs);
+ }
+ return (errs != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-badread.c b/usr/src/test/os-tests/tests/uccid/excl-badread.c
new file mode 100644
index 0000000000..e5f265d1e7
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-badread.c
@@ -0,0 +1,80 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify various bad read conditions fail successfully.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ uccid_cmd_txn_begin_t begin;
+ ssize_t ret;
+ char buf[500];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+
+ /*
+ * Read without having a transaction
+ */
+ ret = read(fd, buf, sizeof (buf));
+ if (ret != -1) {
+ errx(EXIT_FAILURE, "read succeeded when it should have failed "
+ "(EACCES case), returned %ld", ret);
+ }
+
+ if (errno != EACCES) {
+ errx(EXIT_FAILURE, "found wrong value for errno. Expected "
+ "%d, received %d", EACCES, errno);
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ ret = read(fd, buf, sizeof (buf));
+ if (ret != -1) {
+ errx(EXIT_FAILURE, "read succeeded when it should have failed "
+ "(ENODATA case), returned %ld", ret);
+ }
+
+ if (errno != ENODATA) {
+ errx(EXIT_FAILURE, "found wrong value for errno. Expected "
+ "%d, received %d", ENODATA, errno);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-basic.c b/usr/src/test/os-tests/tests/uccid/excl-basic.c
new file mode 100644
index 0000000000..c6cf30dd5e
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-basic.c
@@ -0,0 +1,65 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can grab a basic exclusive lock through an ioctl on the slot.
+ * Then that we can release it afterwards.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ uint_t i;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_txn_end_t end;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ bzero(&end, sizeof (end));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ for (i = 0; i < 10; i++) {
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-close.c b/usr/src/test/os-tests/tests/uccid/excl-close.c
new file mode 100644
index 0000000000..3936c73ab0
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-close.c
@@ -0,0 +1,81 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that if a child grabs an exclusive lock and calls exit, we can grab it
+ * again.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, estat;
+ pid_t pid;
+ uccid_cmd_txn_begin_t begin;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+
+ pid = fork();
+ if (pid == 0) {
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ _exit(0);
+ }
+
+ estat = -1;
+ if (waitpid(pid, &estat, 0) == -1) {
+ err(EXIT_FAILURE, "failed to wait for pid %d", pid);
+ }
+
+ if (estat != 0) {
+ errx(EXIT_FAILURE, "child exited with non-zero value (%d)",
+ estat);
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-loop.c b/usr/src/test/os-tests/tests/uccid/excl-loop.c
new file mode 100644
index 0000000000..f31fc81a34
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-loop.c
@@ -0,0 +1,84 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can grab a basic exclusive lock and then if we try to get
+ * another lock it fails. Regardless of whether we do so through open(2) or
+ * ioctl(2).
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/debug.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_txn_end_t end;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ bzero(&end, sizeof (end));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ ret = ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EEXIST);
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+
+ VERIFY0(close(fd));
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ ret = ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin);
+ VERIFY0(ret);
+
+ ret = ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EEXIST);
+
+ VERIFY0(close(fd));
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-nonblock.c b/usr/src/test/os-tests/tests/uccid/excl-nonblock.c
new file mode 100644
index 0000000000..ee4f1edbd1
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-nonblock.c
@@ -0,0 +1,99 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that if we've grabbed an exclusive lock, another thread fails to grab
+ * it as a non-blocking lock.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/debug.h>
+#include <thread.h>
+#include <errno.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+void *
+nonblock_thread(void *arg)
+{
+ uccid_cmd_txn_begin_t begin;
+ int ret;
+ int fd = (uintptr_t)arg;
+
+
+ bzero(&begin, sizeof (begin));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ begin.uct_flags = UCCID_TXN_DONT_BLOCK;
+
+ ret = ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EBUSY);
+
+ return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fda, fdb;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_txn_end_t end;
+ thread_t thr;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fda = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ if ((fdb = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ bzero(&end, sizeof (end));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ if (ioctl(fda, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ if (thr_create(NULL, 0, nonblock_thread, (void *)(uintptr_t)fdb, 0,
+ &thr) != 0) {
+ err(EXIT_FAILURE, "failed to create thread");
+ }
+
+ if (thr_join(thr, NULL, NULL) != 0) {
+ err(EXIT_FAILURE, "failed to join therad");
+ }
+
+ if (ioctl(fda, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-reset.c b/usr/src/test/os-tests/tests/uccid/excl-reset.c
new file mode 100644
index 0000000000..7ab1718475
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-reset.c
@@ -0,0 +1,65 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can grab a basic exclusive lock through an ioctl on the slot.
+ * Then that we can release it afterwards and reset the ICC.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ uint_t i;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_txn_end_t end;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ bzero(&end, sizeof (end));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RESET;
+
+ for (i = 0; i < 10; i++) {
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/modify.c b/usr/src/test/os-tests/tests/uccid/modify.c
new file mode 100644
index 0000000000..1ac44f3ba4
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/modify.c
@@ -0,0 +1,181 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can issue ICC_MODIFY ioctls. Also, check some of the failure
+ * modes.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/debug.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+static const uint8_t yk_req[] = {
+ 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
+};
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ uccid_cmd_icc_modify_t uci;
+ uccid_cmd_status_t ucs;
+ uccid_cmd_txn_begin_t begin;
+ uint8_t buf[UCCID_APDU_SIZE_MAX];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ /* try power off the card outside of a transaction */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_OFF;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EACCES);
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ /* make sure the card is active (power on) */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, !=, 0);
+
+ /* power off the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_OFF;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is inactive now */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, ==, 0);
+
+ /* power on the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_ON;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is active again */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, !=, 0);
+
+ /* do a warm reset of the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_WARM_RESET;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is still active */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, !=, 0);
+
+ /* write a command to the card, which is assumed to be a YubiKey */
+ if ((ret = write(fd, yk_req, sizeof (yk_req))) < 0) {
+ err(EXIT_FAILURE, "failed to write data");
+ }
+
+ /* power off the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_OFF;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is inactive now */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, ==, 0);
+
+ /* try to read the answer from the YubiKey. */
+ ret = read(fd, buf, sizeof (buf));
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, ENXIO);
+
+ /* power on the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_ON;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is active again */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, !=, 0);
+
+ /* test various failure modes */
+ uci.uci_version = UCCID_VERSION_ONE - 1;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ uci.uci_version = UCCID_VERSION_ONE + 1;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = 0;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = -1;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/notxn-poll.c b/usr/src/test/os-tests/tests/uccid/notxn-poll.c
new file mode 100644
index 0000000000..b45f06b218
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/notxn-poll.c
@@ -0,0 +1,57 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that trying to poll without a transaction / excl access works but
+ * returns no events.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/debug.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ struct pollfd pfds[1];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLIN;
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3S(pfds[0].revents, ==, 0);
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/pollin.c b/usr/src/test/os-tests/tests/uccid/pollin.c
new file mode 100644
index 0000000000..dd81c245cc
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/pollin.c
@@ -0,0 +1,64 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Open up a device and make sure we get pollout by default.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ struct pollfd pfds[1];
+ uccid_cmd_txn_begin_t begin;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLIN;
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ if (ret != 0) {
+ err(EXIT_FAILURE, "poll didn't return 0, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/pollout.c b/usr/src/test/os-tests/tests/uccid/pollout.c
new file mode 100644
index 0000000000..a9928a6fe2
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/pollout.c
@@ -0,0 +1,68 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Open up a device and make sure we get pollout by default.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ struct pollfd pfds[1];
+ uccid_cmd_txn_begin_t begin;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLOUT;
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ if (ret != 1) {
+ err(EXIT_FAILURE, "poll didn't return 1, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ if (!(pfds[0].revents & POLLOUT)) {
+ err(EXIT_FAILURE, "missing pollout, got %d", pfds[0].revents);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/status.c b/usr/src/test/os-tests/tests/uccid/status.c
new file mode 100644
index 0000000000..ae2a51226f
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/status.c
@@ -0,0 +1,97 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can issue various status ioctls regardless of whether or not
+ * we have exclusive access on our handle. Also, check some of the failure
+ * modes.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/debug.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, efd, ret;
+ uccid_cmd_status_t ucs;
+ uccid_cmd_txn_begin_t begin;
+ void *badaddr;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ if ((efd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(efd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+
+ ret = ioctl(efd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+
+ ucs.ucs_version = UCCID_VERSION_ONE - 1;
+ ret = ioctl(efd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ ucs.ucs_version = UCCID_VERSION_ONE + 1;
+ ret = ioctl(efd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ ucs.ucs_version = UCCID_VERSION_ONE - 1;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ ucs.ucs_version = UCCID_VERSION_ONE + 1;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ badaddr = mmap(NULL, PAGESIZE, PROT_READ, MAP_PRIVATE | MAP_ANON, -1,
+ 0);
+ VERIFY3P(badaddr, !=, MAP_FAILED);
+ VERIFY0(munmap(badaddr, PAGESIZE));
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/txn-pollerr.c b/usr/src/test/os-tests/tests/uccid/txn-pollerr.c
new file mode 100644
index 0000000000..b19598f711
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/txn-pollerr.c
@@ -0,0 +1,88 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that closing a transaction while polling generates POLLERR.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/debug.h>
+#include <poll.h>
+#include <port.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, port;
+ uccid_cmd_txn_end_t end;
+ uccid_cmd_txn_begin_t begin;
+ port_event_t pe;
+ timespec_t to;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((port = port_create()) == -1) {
+ err(EXIT_FAILURE, "failed to create event port: %d",
+ port);
+ }
+
+ if ((fd = open(argv[1], O_RDWR | O_EXCL)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ /*
+ * Do not poll for pollout here, since by default, after grabbing a
+ * transaction, the device is writeable.
+ */
+ if (port_associate(port, PORT_SOURCE_FD, fd, POLLIN, NULL) != 0) {
+ err(EXIT_FAILURE, "failed to associate");
+ }
+
+ bzero(&end, sizeof (end));
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+
+ bzero(&to, sizeof (timespec_t));
+ if (port_get(port, &pe, &to) != 0) {
+ err(EXIT_FAILURE, "failed to port_get()");
+ }
+
+ VERIFY3S(pe.portev_source, ==, PORT_SOURCE_FD);
+ VERIFY3S(pe.portev_object, ==, fd);
+ VERIFY3S(pe.portev_events & POLLERR, !=, 0);
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/yk-poll.c b/usr/src/test/os-tests/tests/uccid/yk-poll.c
new file mode 100644
index 0000000000..fb52949f38
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/yk-poll.c
@@ -0,0 +1,108 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Open a YubiKey class device and get the basic information applet
+ * through an APDU while using poll(2) to check device readyness.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+static const uint8_t yk_req[] = {
+ 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
+};
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ struct pollfd pfds[1];
+ uccid_cmd_txn_begin_t begin;
+ uint8_t buf[UCCID_APDU_SIZE_MAX];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLOUT | POLLIN | POLLRDNORM;
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ if (ret != 1) {
+ err(EXIT_FAILURE, "poll didn't return 1, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ if ((pfds[0].revents & POLLOUT) != POLLOUT) {
+ err(EXIT_FAILURE, "expecting pollout, got %d", pfds[0].revents);
+ }
+
+ if ((ret = write(fd, yk_req, sizeof (yk_req))) < 0) {
+ err(EXIT_FAILURE, "failed to write data");
+ }
+
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, -1);
+ if (ret != 1) {
+ err(EXIT_FAILURE, "poll didn't return 1, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ if ((pfds[0].revents & (POLLIN | POLLRDNORM)) !=
+ (POLLIN | POLLRDNORM)) {
+ err(EXIT_FAILURE, "expecting pollin|pollrdnorm, got %d",
+ pfds[0].revents);
+ }
+
+ if ((ret = read(fd, buf, sizeof (buf))) < 0) {
+ err(EXIT_FAILURE, "failed to read data");
+ }
+
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ if (ret != 1) {
+ err(EXIT_FAILURE, "poll didn't return 1, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ if ((pfds[0].revents & POLLOUT) != POLLOUT) {
+ err(EXIT_FAILURE, "expecting pollout, got %d", pfds[0].revents);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/yk-readonly.c b/usr/src/test/os-tests/tests/uccid/yk-readonly.c
new file mode 100644
index 0000000000..dfe7390ea1
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/yk-readonly.c
@@ -0,0 +1,84 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Open a YubiKey class device read-only and try to get the basic information
+ * applet through an APDU, which should fail. Try to get the status, which
+ * should succeed, and attempt to power off, which should fail.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+static const uint8_t yk_req[] = {
+ 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
+};
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ uccid_cmd_icc_modify_t uci;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_status_t ucs;
+ uint8_t buf[UCCID_APDU_SIZE_MAX];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) == 0) {
+ err(EXIT_FAILURE, "didn't fail to issue begin ioctl");
+ }
+
+ if ((ret = write(fd, yk_req, sizeof (yk_req))) != -1) {
+ err(EXIT_FAILURE, "didn't fail to write data");
+ }
+
+ if ((ret = read(fd, buf, sizeof (buf))) != -1) {
+ err(EXIT_FAILURE, "didn't fail to read data");
+ }
+
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ if ((ret = ioctl(fd, UCCID_CMD_STATUS, &ucs)) != 0) {
+ err(EXIT_FAILURE, "failed to get status");
+ }
+
+ /* try power off the card outside of a transaction */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_OFF;
+ if ((ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci)) == 0) {
+ err(EXIT_FAILURE, "didn't fail to power off ICC");
+ }
+
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/yk.c b/usr/src/test/os-tests/tests/uccid/yk.c
new file mode 100644
index 0000000000..45bdfd059e
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/yk.c
@@ -0,0 +1,78 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Attempt to open a YubiKey class device and get the basic information applet
+ * through an APDU.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+static const uint8_t yk_req[] = {
+ 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
+};
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ ssize_t ret, i;
+ uccid_cmd_txn_begin_t begin;
+ uint8_t buf[UCCID_APDU_SIZE_MAX];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ if ((ret = write(fd, yk_req, sizeof (yk_req))) < 0) {
+ err(EXIT_FAILURE, "failed to write data");
+ }
+
+ if ((ret = read(fd, buf, sizeof (buf))) < 0) {
+ err(EXIT_FAILURE, "failed to read data");
+ }
+
+ (void) printf("read %d bytes\n", ret);
+ for (i = 0; i < ret; i++) {
+ (void) printf("%02x", buf[i]);
+ if (i == (ret - 1) || (i % 16) == 15) {
+ (void) printf("\n");
+ } else {
+ (void) printf(" ");
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/cmd/utiltest.ksh b/usr/src/test/util-tests/cmd/utiltest.ksh
index 231415fba8..e701aeb854 100644
--- a/usr/src/test/util-tests/cmd/utiltest.ksh
+++ b/usr/src/test/util-tests/cmd/utiltest.ksh
@@ -17,6 +17,8 @@
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
#
+export PATH="${PATH}:/opt/onbld/bin/$(uname -p)"
+export LC_ALL="C"
export MY_TESTS="/opt/util-tests"
runner="/opt/test-runner/bin/run"
diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index 3170859794..628982c82d 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -35,6 +35,13 @@ outputdir = /var/tmp/test_results
[/opt/util-tests/tests/libjedec_test]
[/opt/util-tests/tests/smbios]
+#
+# The sleep tests require destructive DTrace. Therefore they must be run
+# as root.
+#
+[/opt/util-tests/tests/sleep/sleeptest]
+user = root
+
[/opt/util-tests/tests/xargs_test]
[/opt/util-tests/tests/mergeq/mqt]
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index 6fab960d55..a39762c2fc 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -19,7 +19,7 @@
#
SUBDIRS = date dis dladm iconv libnvpair_json libsff printf xargs grep_xpg4
-SUBDIRS += demangle mergeq workq chown ctf smbios libjedec awk make
+SUBDIRS += demangle mergeq workq chown ctf smbios libjedec awk make sleep
SUBDIRS += bunyan
include $(SRC)/test/Makefile.com
diff --git a/usr/src/test/util-tests/tests/sleep/Makefile b/usr/src/test/util-tests/tests/sleep/Makefile
new file mode 100644
index 0000000000..3cf11e1f40
--- /dev/null
+++ b/usr/src/test/util-tests/tests/sleep/Makefile
@@ -0,0 +1,47 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Robert Mustacchi
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+ROOTOPTPKG = $(ROOT)/opt/util-tests/tests/sleep
+PROG = sleeptest
+DATAFILES = sleep.d sleep.awk
+
+ROOTPROG = $(PROG:%=$(ROOTOPTPKG)/%)
+ROOTDATA = $(DATAFILES:%=$(ROOTOPTPKG)/%)
+
+$(ROOTDATA) := FILEMODE = 0444
+
+all:
+
+install: $(ROOTPROG) $(ROOTDATA)
+
+lint:
+
+clobber: clean
+
+clean:
+
+$(CMDS): $(TESTDIR)
+
+$(ROOTOPTPKG):
+ $(INS.dir)
+
+$(ROOTOPTPKG)/%: %.ksh $(ROOTOPTPKG)
+ $(INS.rename)
+
+$(ROOTOPTPKG)/%: % $(ROOTOPTPKG)
+ $(INS.file)
diff --git a/usr/src/test/util-tests/tests/sleep/sleep.awk b/usr/src/test/util-tests/tests/sleep/sleep.awk
new file mode 100644
index 0000000000..856773c815
--- /dev/null
+++ b/usr/src/test/util-tests/tests/sleep/sleep.awk
@@ -0,0 +1,22 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Robert Mustacchi
+#
+
+{
+ if ($1 == "slp") {
+ if ($2 >= min && $2 <= min * factor) {
+ exit 42
+ }
+ }
+}
diff --git a/usr/src/test/util-tests/tests/sleep/sleep.d b/usr/src/test/util-tests/tests/sleep/sleep.d
new file mode 100644
index 0000000000..23bfcea65c
--- /dev/null
+++ b/usr/src/test/util-tests/tests/sleep/sleep.d
@@ -0,0 +1,40 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Robert Mustacchi
+ */
+
+/*
+ * This is a companion DTrace script that is used by the sleep test. It
+ * checks if the executed program asks to sleep for the right amount of
+ * time and then exits in a way to indicate this. At the same time, it
+ * always uses the SIGALRM feature of sleep(1) to make sure that sleep
+ * doesn't continue executing (and also to make sure that the feature
+ * works).
+ *
+ * We expect the number of seconds in $1 and the number of nanoseconds
+ * in $2. This script should be invoked as dtrace -s sleep.d -c
+ * '/usr/bin/sleep <waittime>' <seconds> <nanoseconds>.
+ */
+pid$target::nanosleep:entry
+/args[0]->tv_sec == $1 && args[0]->tv_nsec == $2/
+{
+ raise(SIGALRM);
+ exit(0);
+}
+
+pid$target::nanosleep:entry
+{
+ print(*args[0]);
+ raise(SIGALRM);
+ exit(1);
+}
diff --git a/usr/src/test/util-tests/tests/sleep/sleeptest.ksh b/usr/src/test/util-tests/tests/sleep/sleeptest.ksh
new file mode 100644
index 0000000000..3cc107acf5
--- /dev/null
+++ b/usr/src/test/util-tests/tests/sleep/sleeptest.ksh
@@ -0,0 +1,180 @@
+#!/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Robert Mustacchi
+#
+
+#
+# Basic tests of sleep(1). sleep is a little hard to test, especially
+# for longer running cases. Therefore to test it, we basically take
+# advantage of our knowledge of how it is implemented. We see that it
+# properly is sleeping for the right amount of time by looking at the
+# call to nanosleep in libc and make sure that the structures time is
+# what we expect.
+#
+
+unalias -a
+set -o pipefail
+
+#
+# Set the locale for the start of the test to be C.UTF-8 to make sure
+# that we have a good starting point and correct fractional
+# interpretation.
+#
+export LC_ALL=C.UTF-8
+
+sleep_arg0="$(basename $0)"
+sleep_prog=/usr/bin/sleep
+sleep_dir="$(dirname $0)"
+sleep_dscript=$sleep_dir/sleep.d
+sleep_awk=$sleep_dir/sleep.awk
+sleep_exit=0
+
+#
+# This is the factor by which we're going to basically say that the slp
+# microstate has to complete within. Because the system will usually
+# have a bit of additional latency, we will usually be greater than that
+# as well. This determines how much we should actually do that by.
+#
+sleep_factor=1.5
+
+warn()
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "TEST FAILED: $sleep_arg0: $msg" >&2
+}
+
+sleep_bound()
+{
+ typeset min=$1
+ typeset test="sleep $min: bounding"
+
+ ptime -m $sleep_prog $min 2>&1 | nawk -f $sleep_awk min=$min \
+ factor=$sleep_factor
+ if [[ $? -ne 42 ]]; then
+ warn "$test"
+ sleep_exit=1
+ else
+ printf "TEST PASSED: %s\n" "$test"
+ fi
+}
+
+sleep_one()
+{
+ typeset arg=$1
+ typeset secs=$2
+ typeset nsecs=$3
+ typeset test="sleep $arg: $secs secs $nsecs ns"
+
+ if ! dtrace -qws $sleep_dscript -c "$sleep_prog $arg" $secs $nsecs; then
+ warn "$test"
+ sleep_exit=1
+ else
+ printf "TEST PASSED: %s\n" "$test"
+ fi
+}
+
+sleep_err()
+{
+ typeset test="negative test: sleep $*"
+
+ if $sleep_prog $* 2>/dev/null; then
+ warn "$test"
+ sleep_exit=1
+ else
+ printf "TEST PASSED: %s\n" "$test"
+ fi
+}
+
+if [[ -n $SLEEP ]]; then
+ sleep_prog=$SLEEP
+fi
+
+#
+# First test basic integer values. Both in base 10 and hex.
+#
+sleep_one 1 1 0
+sleep_one 23 23 0
+sleep_one 0xff 0xff 0
+sleep_one 123456789 123456789 0
+sleep_one 1e8 100000000 0
+
+#
+# Fractional values.
+#
+sleep_one 2.5 2 500000000
+sleep_one 0.9 0 900000000
+sleep_one 34.0051 34 5100000
+sleep_one 0x654.100 0x654 62500000
+
+#
+# Large values that are basically the same as infinity. The current
+# implementation will do a sleep in groups of INT32_MAX at a time. So
+# make sure our large values are the same.
+#
+sleep_one Inf 0x7fffffff 0
+sleep_one +Inf 0x7fffffff 0
+sleep_one 1e100 0x7fffffff 0
+sleep_one 0x123456789abc 0x7fffffff 0
+
+#
+# That all of our suffixes for time increments work and make sense.
+#
+sleep_one 1s 1 0
+sleep_one 1m 60 0
+sleep_one 1h 3600 0
+sleep_one 1d 86400 0
+sleep_one 1w 604800 0
+sleep_one 1y 31536000 0
+
+sleep_one 3.5s 3 500000000
+sleep_one 3.6d 311040 0
+sleep_one 2.001y 63103536 0
+
+#
+# Now we need to go through and use ptime -m to get the slp time for
+# things and make sure it is always greater than what we asked for and
+# less than a bound.
+#
+sleep_bound 0.01
+sleep_bound 0.1
+sleep_bound 0.25
+sleep_bound 0.5
+sleep_bound 0.75
+
+#
+# The next set of tests are negative tests that make sure that sleep
+# does not correctly execute in these cases.
+#
+sleep_err \"\"
+sleep_err 1 2 3
+sleep_err 1@23
+sleep_err 0,56
+sleep_err "hello"
+sleep_err s
+sleep_err 1z
+sleep_err -- -0.3
+
+#
+# Test a locale that uses a ',' character (de_DE.UTF-8 is one) as the
+# decimal point to make sure that sleep correctly is using LC_NUMERIC.
+#
+export LANG=de_DE.UTF-8
+sleep_err 21.45
+sleep_one 2,5 2 500000000
+sleep_one 34,0051 34 5100000
+sleep_one 3,6d 311040 0
+export LANG=C.UTF-8
+
+exit $sleep_exit
diff --git a/usr/src/test/util-tests/tests/smbios/Makefile b/usr/src/test/util-tests/tests/smbios/Makefile
index 5016e81a6d..e783a337dc 100644
--- a/usr/src/test/util-tests/tests/smbios/Makefile
+++ b/usr/src/test/util-tests/tests/smbios/Makefile
@@ -18,10 +18,13 @@ include $(SRC)/Makefile.master
ROOTOPTPKG = $(ROOT)/opt/util-tests
TESTDIR = $(ROOTOPTPKG)/tests/
+OBJS = smbios.o \
+ smbios_test_errors.o \
+ smbios_test_memdevice.o \
+ smbios_test_pinfo.o \
+ smbios_test_slot.o
PROGS = smbios
-LINTS = $(PROGS:%=%.ln)
-
include $(SRC)/cmd/Makefile.cmd
include $(SRC)/test/Makefile.com
@@ -33,16 +36,15 @@ CFLAGS += -_gcc=-Wall -_gcc=-Wextra -_gcc=-Wno-unknown-pragmas
all: $(PROGS)
-install: all $(CMDS) $(OUTFILES)
+install: all $(CMDS)
clobber: clean
- -$(RM) $(PROGS) $(LINTS)
-
-lint:
+ -$(RM) $(PROGS)
clean:
+ -$(RM) $(OBJS)
-$(CMDS): $(TESTDIR) $(PROG)
+$(CMDS): $(TESTDIR) $(PROGS)
$(TESTDIR):
$(INS.dir)
@@ -50,6 +52,6 @@ $(TESTDIR):
$(TESTDIR)/%: %
$(INS.file)
-%: %.c
- $(LINK.c) -o $@ $< $(LDLIBS)
+$(PROGS): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
$(POST_PROCESS)
diff --git a/usr/src/test/util-tests/tests/smbios/smbios.c b/usr/src/test/util-tests/tests/smbios/smbios.c
index 96b887c3cc..364cb9344f 100644
--- a/usr/src/test/util-tests/tests/smbios/smbios.c
+++ b/usr/src/test/util-tests/tests/smbios/smbios.c
@@ -19,33 +19,8 @@
* This should be evolved into a much fuller test suite.
*/
-#include <smbios.h>
#include <umem.h>
-#include <stdint.h>
-#include <endian.h>
-#include <stdio.h>
-#include <err.h>
-
-#include <sys/smbios.h>
-#include <sys/smbios_impl.h>
-
-static const char *smbios_test_name = "The One Slot";
-
-/*
- * Number of bytes we allocate at a given time for an SMBIOS table.
- */
-#define SMBIOS_TEST_ALLOC_SIZE 1024
-
-typedef struct smbios_test_table {
- smbios_entry_point_t stt_type;
- void *stt_data;
- size_t stt_buflen;
- size_t stt_offset;
- uint_t stt_nents;
- uint_t stt_version;
- uint_t stt_nextid;
- smbios_entry_t stt_entry;
-} smbios_test_table_t;
+#include "smbios_test.h"
const char *
_umem_debug_init(void)
@@ -59,7 +34,7 @@ _umem_logging_init(void)
return ("fail,contents");
}
-static smbios_test_table_t *
+smbios_test_table_t *
smbios_test_table_init(smbios_entry_point_t type, uint_t version)
{
smbios_test_table_t *table;
@@ -122,21 +97,21 @@ smbios_test_table_append_common(smbios_test_table_t *table, const void *buf,
return (start);
}
-static void
+void
smbios_test_table_append_raw(smbios_test_table_t *table, const void *buf,
size_t len)
{
(void) smbios_test_table_append_common(table, buf, len);
}
-static void
+void
smbios_test_table_append_string(smbios_test_table_t *table, const char *str)
{
size_t len = strlen(str) + 1;
(void) smbios_test_table_append_common(table, str, len);
}
-static uint16_t
+uint16_t
smbios_test_table_append(smbios_test_table_t *table, const void *buf,
size_t len)
{
@@ -153,6 +128,25 @@ smbios_test_table_append(smbios_test_table_t *table, const void *buf,
return (id);
}
+void
+smbios_test_table_append_eot(smbios_test_table_t *table)
+{
+ smb_header_t eot;
+ uint8_t endstring = 0;
+
+ bzero(&eot, sizeof (eot));
+ eot.smbh_type = SMB_TYPE_EOT;
+ eot.smbh_len = 4;
+ (void) smbios_test_table_append(table, &eot, sizeof (eot));
+ (void) smbios_test_table_append_raw(table, &endstring,
+ sizeof (endstring));
+ smbios_test_table_append_raw(table, &endstring,
+ sizeof (endstring));
+ smbios_test_table_append_raw(table, &endstring,
+ sizeof (endstring));
+
+}
+
static uint8_t
smbios_test_table_checksum(const uint8_t *buf, size_t len)
{
@@ -217,195 +211,181 @@ smbios_test_table_fini(smbios_test_table_t *table)
umem_free(table, sizeof (smbios_test_table_t));
}
-static void
-smbios_test_mktable(smbios_test_table_t *table)
+static const smbios_test_t smbios_tests[] = {
+ {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION_32,
+ .st_libvers = SMB_VERSION,
+ .st_mktable = smbios_test_slot_mktable,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_slot_verify,
+ .st_desc = "slot tests"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION,
+ .st_libvers = 0xffff,
+ .st_mktable = smbios_test_badvers_mktable,
+ .st_desc = "bad library version"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION_32,
+ .st_libvers = SMB_VERSION_32,
+ .st_mktable = smbios_test_memdevice_mktable_32,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_memdevice_verify_32,
+ .st_desc = "memory device 3.2 / 3.2"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION_32,
+ .st_libvers = SMB_VERSION_33,
+ .st_mktable = smbios_test_memdevice_mktable_32,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_memdevice_verify_32_33,
+ .st_desc = "memory device 3.2 / 3.3"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION_33,
+ .st_libvers = SMB_VERSION_33,
+ .st_mktable = smbios_test_memdevice_mktable_33,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_memdevice_verify_33,
+ .st_desc = "memory device 3.3"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION_33,
+ .st_libvers = SMB_VERSION_33,
+ .st_mktable = smbios_test_memdevice_mktable_33ext,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_memdevice_verify_33ext,
+ .st_desc = "memory device 3.3"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION,
+ .st_libvers = SMB_VERSION,
+ .st_mktable = smbios_test_pinfo_mktable_amd64,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_pinfo_verify_amd64,
+ .st_desc = "processor additional information - amd64"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION,
+ .st_libvers = SMB_VERSION,
+ .st_mktable = smbios_test_pinfo_mktable_riscv,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_pinfo_verify_riscv,
+ .st_desc = "processor additional information - riscv"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION,
+ .st_libvers = SMB_VERSION,
+ .st_mktable = smbios_test_pinfo_mktable_invlen1,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_pinfo_verify_invlen1,
+ .st_desc = "processor additional information - bad table length 1"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION,
+ .st_libvers = SMB_VERSION,
+ .st_mktable = smbios_test_pinfo_mktable_invlen2,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_pinfo_verify_invlen2,
+ .st_desc = "processor additional information - bad table length 2"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION,
+ .st_libvers = SMB_VERSION,
+ .st_mktable = smbios_test_pinfo_mktable_invlen3,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_pinfo_verify_invlen3,
+ .st_desc = "processor additional information - bad table length 3"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION,
+ .st_libvers = SMB_VERSION,
+ .st_mktable = smbios_test_pinfo_mktable_invlen4,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_pinfo_verify_invlen4,
+ .st_desc = "processor additional information - bad table length 4"
+ }, {
+ .st_entry = SMBIOS_ENTRY_POINT_30,
+ .st_tvers = SMB_VERSION,
+ .st_libvers = SMB_VERSION,
+ .st_mktable = smbios_test_memdevice_mktable_32,
+ .st_canopen = B_TRUE,
+ .st_verify = smbios_test_pinfo_verify_badtype,
+ .st_desc = "processor additional information - bad type"
+ },
+
+};
+
+static boolean_t
+smbios_test_run_one(const smbios_test_t *test)
{
- smb_slot_t slot;
- smb_slot_peer_t peers[2];
- smb_header_t eot;
- uint8_t endstring = 0;
-
- slot.smbsl_hdr.smbh_type = SMB_TYPE_SLOT;
- slot.smbsl_hdr.smbh_len = sizeof (smb_slot_t) + sizeof (peers);
-
- slot.smbsl_name = 1;
- slot.smbsl_type = SMB_SLT_PCIE3G16;
- slot.smbsl_width = SMB_SLW_16X;
- slot.smbsl_length = SMB_SLL_SHORT;
- slot.smbsl_id = htole16(1);
- slot.smbsl_ch1 = SMB_SLCH1_33V;
- slot.smbsl_ch2 = SMB_SLCH2_PME;
- slot.smbsl_sg = htole16(1);
- slot.smbsl_bus = 0x42;
- slot.smbsl_df = 0x23;
- slot.smbsl_dbw = SMB_SLW_16X;
- slot.smbsl_npeers = 2;
- peers[0].smbspb_group_no = htole16(1);
- peers[0].smbspb_bus = 0x42;
- peers[0].smbspb_df = 0x42;
- peers[0].smbspb_width = SMB_SLW_8X;
-
- peers[1].smbspb_group_no = htole16(1);
- peers[1].smbspb_bus = 0x23;
- peers[1].smbspb_df = 0x31;
- peers[1].smbspb_width = SMB_SLW_8X;
-
- (void) smbios_test_table_append(table, &slot, sizeof (slot));
- (void) smbios_test_table_append_raw(table, peers, sizeof (peers));
- (void) smbios_test_table_append_string(table, smbios_test_name);
- (void) smbios_test_table_append_raw(table, &endstring,
- sizeof (endstring));
-
- bzero(&eot, sizeof (eot));
- eot.smbh_type = SMB_TYPE_EOT;
- eot.smbh_len = 4;
- (void) smbios_test_table_append(table, &eot, sizeof (eot));
- (void) smbios_test_table_append_raw(table, &endstring,
- sizeof (endstring));
- (void) smbios_test_table_append_raw(table, &endstring,
- sizeof (endstring));
-}
-
-static void
-smbios_test_verify_table(smbios_hdl_t *hdl)
-{
- smbios_struct_t sp;
- smbios_slot_t slot;
- uint_t npeers;
- smbios_slot_peer_t *peers;
- uint_t errs = 0;
-
- if (smbios_lookup_type(hdl, SMB_TYPE_SLOT, &sp) == -1) {
- errx(EXIT_FAILURE, "failed to lookup SMBIOS slot: %s",
- smbios_errmsg(smbios_errno(hdl)));
- }
-
- if (smbios_info_slot(hdl, sp.smbstr_id, &slot) != 0) {
- errx(EXIT_FAILURE, "failed to get SMBIOS slot info: %s",
- smbios_errmsg(smbios_errno(hdl)));
- }
-
- /*
- * Verify everything we'd expect about the slot.
- */
- if (strcmp(slot.smbl_name, smbios_test_name) != 0) {
- warnx("slot name mismatch, expected %s, found %s",
- smbios_test_name, slot.smbl_name);
- errs++;
- }
-
- if (slot.smbl_type != SMB_SLT_PCIE3G16) {
- warnx("incorrect slot type, found %u", slot.smbl_type);
- errs++;
- }
-
- if (slot.smbl_width != SMB_SLW_16X) {
- warnx("incorrect slot width, found %u", slot.smbl_width);
- errs++;
- }
-
- if (slot.smbl_length != SMB_SLL_SHORT) {
- warnx("incorrect slot length, found %u", slot.smbl_length);
- errs++;
- }
-
- if (slot.smbl_dbw != SMB_SLW_16X) {
- warnx("incorrect slot data bus width, found %u", slot.smbl_dbw);
- errs++;
- }
-
- if (slot.smbl_npeers != 2) {
- warnx("incorrect number of slot peers, found %u",
- slot.smbl_npeers);
- errs++;
- }
-
- if (smbios_info_slot_peers(hdl, sp.smbstr_id, &npeers, &peers) != 0) {
- errx(EXIT_FAILURE, "failed to get SMBIOS peer info: %s",
- smbios_errmsg(smbios_errno(hdl)));
- }
-
- if (npeers != 2) {
- errx(EXIT_FAILURE, "got wrong number of slot peers: %u\n",
- npeers);
- }
-
- if (peers[0].smblp_group != 1) {
- warnx("incorrect group for peer 0: %u", peers[0].smblp_group);
- errs++;
- }
-
- if (peers[0].smblp_data_width != SMB_SLW_8X) {
- warnx("incorrect data width for peer 0: %u",
- peers[0].smblp_data_width);
- errs++;
- }
+ smbios_test_table_t *table = NULL;
+ smbios_hdl_t *hdl = NULL;
+ void *buf;
+ size_t len;
+ smbios_entry_t *entry;
+ int err = 0;
+ boolean_t ret = B_FALSE;
- if (peers[0].smblp_device != (0x42 >> 3)) {
- warnx("incorrect PCI device for peer 0: %u",
- peers[0].smblp_device);
- errs++;
+ table = smbios_test_table_init(test->st_entry, test->st_tvers);
+ if (!test->st_mktable(table)) {
+ goto out;
}
- if (peers[0].smblp_function != (0x42 & 0x7)) {
- warnx("incorrect PCI function for peer 0: %u",
- peers[0].smblp_function);
- errs++;
+ smbios_test_table_snapshot(table, &entry, &buf, &len);
+ hdl = smbios_bufopen(entry, buf, len, test->st_libvers, SMB_FL_DEBUG,
+ &err);
+ if (test->st_canopen) {
+ if (hdl == NULL) {
+ warnx("failed to create table for test %s: %s",
+ test->st_desc, smbios_errmsg(err));
+ goto out;
+ }
+ } else {
+ if (hdl != NULL) {
+ warnx("accidentally created table for test %s, "
+ "expected failure", test->st_desc);
+ } else {
+ ret = B_TRUE;
+ }
+ goto out;
}
- if (peers[1].smblp_group != 1) {
- warnx("incorrect group for peer 1: %u", peers[1].smblp_group);
- errs++;
+ if (test->st_verify(hdl)) {
+ ret = B_TRUE;
}
- if (peers[1].smblp_device != (0x31 >> 3)) {
- warnx("incorrect PCI device for peer 1: %u",
- peers[1].smblp_device);
- errs++;
+out:
+ if (hdl != NULL) {
+ smbios_close(hdl);
}
- if (peers[1].smblp_function != (0x31 & 0x7)) {
- warnx("incorrect PCI function for peer 1: %u",
- peers[1].smblp_function);
- errs++;
+ if (table != NULL) {
+ smbios_test_table_fini(table);
}
- if (peers[1].smblp_data_width != SMB_SLW_8X) {
- warnx("incorrect data width for peer 1: %u",
- peers[1].smblp_data_width);
- errs++;
+ if (ret) {
+ (void) printf("TEST PASSED: %s\n", test->st_desc);
+ } else {
+ (void) printf("TEST FAILED: %s\n", test->st_desc);
}
- smbios_info_slot_peers_free(hdl, npeers, peers);
-
- if (errs > 0) {
- errx(EXIT_FAILURE, "encountered fatal errors");
- }
+ return (ret);
}
int
main(void)
{
- void *buf;
- size_t len;
- smbios_test_table_t *table;
- smbios_entry_t *entry;
- smbios_hdl_t *hdl;
int err = 0;
+ size_t i;
- table = smbios_test_table_init(SMBIOS_ENTRY_POINT_30, SMB_VERSION_32);
- smbios_test_mktable(table);
- smbios_test_table_snapshot(table, &entry, &buf, &len);
-
- hdl = smbios_bufopen(entry, buf, len, SMB_VERSION_32, SMB_FL_DEBUG,
- &err);
- if (hdl == NULL) {
- errx(EXIT_FAILURE, "failed to create fake smbios table: %s",
- smbios_errmsg(err));
+ for (i = 0; i < ARRAY_SIZE(smbios_tests); i++) {
+ if (!smbios_test_run_one(&smbios_tests[i])) {
+ err = 1;
+ }
}
- smbios_test_verify_table(hdl);
- smbios_close(hdl);
- smbios_test_table_fini(table);
- return (0);
+ return (err);
}
diff --git a/usr/src/test/util-tests/tests/smbios/smbios_test.h b/usr/src/test/util-tests/tests/smbios/smbios_test.h
new file mode 100644
index 0000000000..62baed7813
--- /dev/null
+++ b/usr/src/test/util-tests/tests/smbios/smbios_test.h
@@ -0,0 +1,110 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Robert Mustacchi
+ */
+
+#ifndef _SMBIOS_TEST_H
+#define _SMBIOS_TEST_H
+
+/*
+ * Test framework for SMBIOS tests
+ */
+
+#include <smbios.h>
+#include <sys/smbios.h>
+#include <sys/smbios_impl.h>
+#include <err.h>
+#include <stdint.h>
+#include <endian.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Number of bytes we allocate at a given time for an SMBIOS table.
+ */
+#define SMBIOS_TEST_ALLOC_SIZE 1024
+
+typedef struct smbios_test_table {
+ smbios_entry_point_t stt_type;
+ void *stt_data;
+ size_t stt_buflen;
+ size_t stt_offset;
+ uint_t stt_nents;
+ uint_t stt_version;
+ uint_t stt_nextid;
+ smbios_entry_t stt_entry;
+} smbios_test_table_t;
+
+/*
+ * General Interfaces used to construct tables.
+ */
+extern smbios_test_table_t *smbios_test_table_init(smbios_entry_point_t,
+ uint_t);
+extern void smbios_test_table_append_raw(smbios_test_table_t *, const void *,
+ size_t);
+extern void smbios_test_table_append_string(smbios_test_table_t *,
+ const char *);
+extern uint16_t smbios_test_table_append(smbios_test_table_t *, const void *,
+ size_t);
+extern void smbios_test_table_append_eot(smbios_test_table_t *);
+
+typedef boolean_t (*smbios_test_mktable_f)(smbios_test_table_t *);
+typedef boolean_t (*smbios_test_verify_f)(smbios_hdl_t *);
+
+typedef struct smbios_test {
+ int st_entry;
+ int st_tvers;
+ int st_libvers;
+ smbios_test_mktable_f st_mktable;
+ boolean_t st_canopen;
+ smbios_test_verify_f st_verify;
+ const char *st_desc;
+} smbios_test_t;
+
+/*
+ * Test functions
+ */
+extern boolean_t smbios_test_slot_mktable(smbios_test_table_t *);
+extern boolean_t smbios_test_slot_verify(smbios_hdl_t *);
+extern boolean_t smbios_test_badvers_mktable(smbios_test_table_t *);
+
+extern boolean_t smbios_test_memdevice_mktable_32(smbios_test_table_t *);
+extern boolean_t smbios_test_memdevice_mktable_33(smbios_test_table_t *);
+extern boolean_t smbios_test_memdevice_mktable_33ext(smbios_test_table_t *);
+extern boolean_t smbios_test_memdevice_verify_32(smbios_hdl_t *);
+extern boolean_t smbios_test_memdevice_verify_32_33(smbios_hdl_t *);
+extern boolean_t smbios_test_memdevice_verify_33(smbios_hdl_t *);
+extern boolean_t smbios_test_memdevice_verify_33ext(smbios_hdl_t *);
+
+extern boolean_t smbios_test_pinfo_mktable_amd64(smbios_test_table_t *);
+extern boolean_t smbios_test_pinfo_verify_amd64(smbios_hdl_t *);
+extern boolean_t smbios_test_pinfo_mktable_riscv(smbios_test_table_t *);
+extern boolean_t smbios_test_pinfo_verify_riscv(smbios_hdl_t *);
+extern boolean_t smbios_test_pinfo_mktable_invlen1(smbios_test_table_t *);
+extern boolean_t smbios_test_pinfo_mktable_invlen2(smbios_test_table_t *);
+extern boolean_t smbios_test_pinfo_mktable_invlen3(smbios_test_table_t *);
+extern boolean_t smbios_test_pinfo_mktable_invlen4(smbios_test_table_t *);
+extern boolean_t smbios_test_pinfo_verify_invlen1(smbios_hdl_t *);
+extern boolean_t smbios_test_pinfo_verify_invlen2(smbios_hdl_t *);
+extern boolean_t smbios_test_pinfo_verify_invlen3(smbios_hdl_t *);
+extern boolean_t smbios_test_pinfo_verify_invlen4(smbios_hdl_t *);
+extern boolean_t smbios_test_pinfo_verify_badtype(smbios_hdl_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMBIOS_TEST_H */
diff --git a/usr/src/test/util-tests/tests/smbios/smbios_test_errors.c b/usr/src/test/util-tests/tests/smbios/smbios_test_errors.c
new file mode 100644
index 0000000000..0bca13c7bb
--- /dev/null
+++ b/usr/src/test/util-tests/tests/smbios/smbios_test_errors.c
@@ -0,0 +1,28 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Robert Mustacchi
+ */
+
+/*
+ * Collection of functions to be used with tests that will cause a handle to
+ * fail to open.
+ */
+
+#include "smbios_test.h"
+
+boolean_t
+smbios_test_badvers_mktable(smbios_test_table_t *table)
+{
+ smbios_test_table_append_eot(table);
+ return (B_TRUE);
+}
diff --git a/usr/src/test/util-tests/tests/smbios/smbios_test_memdevice.c b/usr/src/test/util-tests/tests/smbios/smbios_test_memdevice.c
new file mode 100644
index 0000000000..7e6e3b02d4
--- /dev/null
+++ b/usr/src/test/util-tests/tests/smbios/smbios_test_memdevice.c
@@ -0,0 +1,355 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Robert Mustacchi
+ */
+
+/*
+ * Basic testing of the SMBIOS 3.3 memory device extensions. We test these in a
+ * few different ways:
+ *
+ * 1. Using a 3.2 table with a 3.2 library to make sure we get the old fields.
+ * We also need to verify that we don't clobber memory in this case.
+ * 2. Using a 3.2 table with a 3.3 library to make sure we get the new fields.
+ * populated with the corresponding 3.2 values.
+ * 3. Using a 3.3 table with only the old values as valid.
+ * 4. Using a 3.3 table with both the old and new values as valid.
+ * memory.
+ */
+
+#include <stdlib.h>
+#include "smbios_test.h"
+
+static uint16_t smbios_memdevice_speed = 0xdeed;
+static uint16_t smbios_memdevice_clkspeed = 0xf00f;
+static uint32_t smbios_memdevice_extspeed = 0xbaddeed;
+static uint32_t smbios_memdevice_extclkspeed = 0xbadf00f;
+
+/*
+ * Fill in the basics of a single memory device. Callers need to fill in the
+ * speed, extspeed, clkspeed, and extclkspeed members.
+ */
+static void
+smbios_test_memdevice_fill(smb_memdevice_t *mem)
+{
+ mem->smbmdev_hdr.smbh_type = SMB_TYPE_MEMDEVICE;
+ mem->smbmdev_hdr.smbh_len = sizeof (smb_memdevice_t);
+
+ mem->smbmdev_array = 0xffff;
+ mem->smbmdev_error = htole16(0xfffe);
+ mem->smbmdev_twidth = 64;
+ mem->smbmdev_dwidth = 64;
+ mem->smbmdev_size = 0x7fff;
+ mem->smbmdev_form = SMB_MDFF_FBDIMM;
+ mem->smbmdev_set = 0;
+ mem->smbmdev_dloc = 0;
+ mem->smbmdev_bloc = 0;
+ mem->smbmdev_type = SMB_MDT_DDR4;
+ mem->smbmdev_manufacturer = 0;
+ mem->smbmdev_asset = 0;
+ mem->smbmdev_part = 0;
+ mem->smbmdev_attrs = 2;
+ mem->smbmdev_extsize = htole32(0x123456);
+ mem->smbmdev_minvolt = 0;
+ mem->smbmdev_maxvolt = 0;
+ mem->smbmdev_confvolt = 0;
+ mem->smbmdev_memtech = 0;
+ mem->smbmdev_opmode = 1 << 3;
+ mem->smbmdev_fwver = 0;
+ mem->smbmdev_modulemfgid = 0;
+ mem->smbmdev_moduleprodid = 0;
+ mem->smbmdev_memsysmfgid = 0;
+ mem->smbmdev_memsysprodid = 0;
+ mem->smbmdev_nvsize = htole64(UINT64_MAX);
+ mem->smbmdev_volsize = htole64(UINT64_MAX);
+ mem->smbmdev_cachesize = htole64(UINT64_MAX);
+ mem->smbmdev_logicalsize = htole64(UINT64_MAX);
+}
+
+boolean_t
+smbios_test_memdevice_mktable_32(smbios_test_table_t *table)
+{
+ smb_memdevice_t mem;
+ size_t len = 0x54;
+
+ smbios_test_memdevice_fill(&mem);
+ mem.smbmdev_speed = htole16(smbios_memdevice_speed);
+ mem.smbmdev_clkspeed = htole16(smbios_memdevice_clkspeed);
+ mem.smbmdev_extspeed = htole32(0);
+ mem.smbmdev_extclkspeed = htole32(0);
+
+ /*
+ * Because we're emulating an SMBIOS 3.2 table, we have to set it to the
+ * specification's defined size for that revision - 0x54.
+ */
+ mem.smbmdev_hdr.smbh_len = len;
+ (void) smbios_test_table_append(table, &mem, len);
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+boolean_t
+smbios_test_memdevice_mktable_33(smbios_test_table_t *table)
+{
+ smb_memdevice_t mem;
+
+ smbios_test_memdevice_fill(&mem);
+ mem.smbmdev_speed = htole16(smbios_memdevice_speed);
+ mem.smbmdev_clkspeed = htole16(smbios_memdevice_clkspeed);
+ mem.smbmdev_extspeed = htole32(0);
+ mem.smbmdev_extclkspeed = htole32(0);
+
+ (void) smbios_test_table_append(table, &mem, sizeof (mem));
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+boolean_t
+smbios_test_memdevice_mktable_33ext(smbios_test_table_t *table)
+{
+ smb_memdevice_t mem;
+
+ smbios_test_memdevice_fill(&mem);
+ mem.smbmdev_speed = htole16(0xffff);
+ mem.smbmdev_clkspeed = htole16(0xffff);
+ mem.smbmdev_extspeed = htole32(smbios_memdevice_extspeed);
+ mem.smbmdev_extclkspeed = htole32(smbios_memdevice_extclkspeed);
+
+ (void) smbios_test_table_append(table, &mem, sizeof (mem));
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+static boolean_t
+smbios_test_memdevice_verify_common(smbios_memdevice_t *mem)
+{
+ boolean_t ret = B_TRUE;
+
+ if (mem->smbmd_dwidth != 64) {
+ warnx("found wrong dwidth: %u", mem->smbmd_dwidth);
+ ret = B_FALSE;
+ }
+
+ if (mem->smbmd_twidth != 64) {
+ warnx("found wrong twidth: %u", mem->smbmd_twidth);
+ ret = B_FALSE;
+ }
+
+ if (mem->smbmd_form != SMB_MDFF_FBDIMM) {
+ warnx("found wrong form: %u", mem->smbmd_form);
+ ret = B_FALSE;
+ }
+
+ if (mem->smbmd_size != 0x123456ULL * 1024 * 1024) {
+ warnx("found wrong size: %u", mem->smbmd_size);
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+boolean_t
+smbios_test_memdevice_verify_32(smbios_hdl_t *hdl)
+{
+ smbios_struct_t sp;
+ smbios_memdevice_t mem;
+ boolean_t ret = B_TRUE;
+ uint64_t rval;
+
+ /*
+ * We expect that the SMBIOS 3.2 memory device values should not be
+ * touched here. As such we set them to a random value to verify and
+ * verify that it hasn't been set.
+ */
+ arc4random_buf(&rval, sizeof (rval));
+ mem.smbmd_extspeed = rval;
+ mem.smbmd_extclkspeed = rval;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
+ warnx("failed to lookup SMBIOS memory device: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
+ warnx("failed to get SMBIOS memory device info: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (mem.smbmd_extspeed != rval || mem.smbmd_extclkspeed != rval) {
+ warnx("smbios_memdevice_t had its memory cloberred!");
+ return (B_FALSE);
+ }
+
+ if (!smbios_test_memdevice_verify_common(&mem)) {
+ return (B_FALSE);
+ }
+
+ if (mem.smbmd_speed != smbios_memdevice_speed) {
+ warnx("found wrong device speed: %u", mem.smbmd_speed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_clkspeed != smbios_memdevice_clkspeed) {
+ warnx("found wrong device clkspeed: %u", mem.smbmd_clkspeed);
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+/*
+ * This is a variant of smbios_test_memdevice_verify_32(), but instead of using
+ * an SMBIOS 3.2 library, we use an SMBIOS 3.3 handle. This means that we expect
+ * the extended values to be populated with the base values.
+ */
+boolean_t
+smbios_test_memdevice_verify_32_33(smbios_hdl_t *hdl)
+{
+ smbios_struct_t sp;
+ smbios_memdevice_t mem;
+ boolean_t ret = B_TRUE;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
+ warnx("failed to lookup SMBIOS memory device: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
+ warnx("failed to get SMBIOS memory device info: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (!smbios_test_memdevice_verify_common(&mem)) {
+ return (B_FALSE);
+ }
+
+ if (mem.smbmd_speed != smbios_memdevice_speed) {
+ warnx("found wrong device speed: %u", mem.smbmd_speed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_clkspeed != smbios_memdevice_clkspeed) {
+ warnx("found wrong device clkspeed: %u", mem.smbmd_clkspeed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_extspeed != smbios_memdevice_speed) {
+ warnx("found wrong device speed: %u", mem.smbmd_extspeed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_extclkspeed != smbios_memdevice_clkspeed) {
+ warnx("found wrong device clkspeed: %u", mem.smbmd_extclkspeed);
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+boolean_t
+smbios_test_memdevice_verify_33(smbios_hdl_t *hdl)
+{
+ smbios_struct_t sp;
+ smbios_memdevice_t mem;
+ boolean_t ret = B_TRUE;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
+ warnx("failed to lookup SMBIOS memory device: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
+ warnx("failed to get SMBIOS memory device info: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (!smbios_test_memdevice_verify_common(&mem)) {
+ return (B_FALSE);
+ }
+
+ if (mem.smbmd_speed != smbios_memdevice_speed) {
+ warnx("found wrong device speed: %u", mem.smbmd_speed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_clkspeed != smbios_memdevice_clkspeed) {
+ warnx("found wrong device clkspeed: %u", mem.smbmd_clkspeed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_extspeed != smbios_memdevice_speed) {
+ warnx("found wrong device speed: %u", mem.smbmd_extspeed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_extclkspeed != smbios_memdevice_clkspeed) {
+ warnx("found wrong device clkspeed: %u", mem.smbmd_extclkspeed);
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+boolean_t
+smbios_test_memdevice_verify_33ext(smbios_hdl_t *hdl)
+{
+ smbios_struct_t sp;
+ smbios_memdevice_t mem;
+ boolean_t ret = B_TRUE;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
+ warnx("failed to lookup SMBIOS memory device: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
+ warnx("failed to get SMBIOS memory device info: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (!smbios_test_memdevice_verify_common(&mem)) {
+ return (B_FALSE);
+ }
+
+ if (mem.smbmd_speed != 0xffff) {
+ warnx("found wrong device speed: %u", mem.smbmd_speed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_clkspeed != 0xffff) {
+ warnx("found wrong device clkspeed: %u", mem.smbmd_clkspeed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_extspeed != smbios_memdevice_extspeed) {
+ warnx("found wrong device speed: %u", mem.smbmd_extspeed);
+ ret = B_FALSE;
+ }
+
+ if (mem.smbmd_extclkspeed != smbios_memdevice_extclkspeed) {
+ warnx("found wrong device clkspeed: %u", mem.smbmd_extclkspeed);
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
diff --git a/usr/src/test/util-tests/tests/smbios/smbios_test_pinfo.c b/usr/src/test/util-tests/tests/smbios/smbios_test_pinfo.c
new file mode 100644
index 0000000000..178de212ae
--- /dev/null
+++ b/usr/src/test/util-tests/tests/smbios/smbios_test_pinfo.c
@@ -0,0 +1,585 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Robert Mustacchi
+ */
+
+/*
+ * Tests for SMBIOS type 44 - SMB_TYPE_PROCESSOR_INFO and the per-CPU type
+ * follow ups.
+ */
+
+#include "smbios_test.h"
+
+static uint16_t smbios_pinfo_phandle = 0x1;
+static uint64_t smbios_pinfo_isa = 0x4010d;
+static uint8_t smbios_pinfo_hartid[16];
+static uint8_t smbios_pinfo_vendid[16];
+static uint8_t smbios_pinfo_archid[16];
+static uint8_t smbios_pinfo_machid[16];
+static uint8_t smbios_pinfo_metdi[16];
+static uint8_t smbios_pinfo_mitdi[16];
+
+boolean_t
+smbios_test_pinfo_mktable_amd64(smbios_test_table_t *table)
+{
+ smb_processor_info_t pi;
+
+ pi.smbpai_hdr.smbh_type = SMB_TYPE_PROCESSOR_INFO;
+ pi.smbpai_hdr.smbh_len = sizeof (smb_processor_info_t);
+ pi.smbpai_proc = htole16(smbios_pinfo_phandle);
+ pi.smbpai_len = 0;
+ pi.smbpai_type = SMB_PROCINFO_T_AMD64;
+
+ (void) smbios_test_table_append(table, &pi, sizeof (pi));
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+boolean_t
+smbios_test_pinfo_verify_amd64(smbios_hdl_t *hdl)
+{
+ smbios_struct_t sp;
+ smbios_processor_info_t pinfo;
+ smbios_processor_info_riscv_t rv;
+ boolean_t ret = B_TRUE;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_PROCESSOR_INFO, &sp) == -1) {
+ warnx("failed to lookup SMBIOS processor additional "
+ "information: %s", smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (smbios_info_processor_info(hdl, sp.smbstr_id, &pinfo) != 0) {
+
+ warnx("failed to get SMBIOS processor additional "
+ "information: %s", smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (pinfo.smbpi_processor != smbios_pinfo_phandle) {
+ warnx("processor handle incorrect, found 0x%x",
+ pinfo.smbpi_processor);
+ ret = B_FALSE;
+ }
+
+ if (pinfo.smbpi_ptype != SMB_PROCINFO_T_AMD64) {
+ warnx("processor type incorrect, found 0x%x",
+ pinfo.smbpi_ptype);
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_processor_info_type_desc(pinfo.smbpi_ptype),
+ "X64 (x86-64, Intel64, AMD64, EMT64)") != 0) {
+ warnx("processor type incorrect, found %s",
+ smbios_processor_info_type_desc(pinfo.smbpi_ptype));
+ ret = B_FALSE;
+ }
+
+ if (smbios_info_processor_riscv(hdl, sp.smbstr_id, &rv) != -1) {
+ warnx("accidentally got riscv info on non-riscv handle");
+ ret = B_FALSE;
+ }
+
+ if (smbios_errno(hdl) != ESMB_TYPE) {
+ warnx("encountered wrong errno for RISC-V info, found: 0x%x",
+ smbios_errno(hdl));
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+boolean_t
+smbios_test_pinfo_mktable_riscv(smbios_test_table_t *table)
+{
+ smb_processor_info_t pi;
+ smb_processor_info_riscv_t rv;
+
+ pi.smbpai_hdr.smbh_type = SMB_TYPE_PROCESSOR_INFO;
+ pi.smbpai_hdr.smbh_len = sizeof (smb_processor_info_t) +
+ sizeof (smb_processor_info_riscv_t);
+ pi.smbpai_proc = htole16(smbios_pinfo_phandle);
+ pi.smbpai_len = sizeof (smb_processor_info_riscv_t);
+ pi.smbpai_type = SMB_PROCINFO_T_RV64;
+
+ (void) smbios_test_table_append(table, &pi, sizeof (pi));
+
+ rv.smbpairv_vers = 1;
+ rv.smbpairv_len = sizeof (smb_processor_info_riscv_t);
+ arc4random_buf(smbios_pinfo_hartid, sizeof (smbios_pinfo_hartid));
+ bcopy(smbios_pinfo_hartid, rv.smbpairv_hartid,
+ sizeof (smbios_pinfo_hartid));
+ rv.smbpairv_boot = 1;
+ arc4random_buf(smbios_pinfo_vendid, sizeof (smbios_pinfo_vendid));
+ bcopy(smbios_pinfo_vendid, rv.smbpairv_vendid,
+ sizeof (smbios_pinfo_vendid));
+ arc4random_buf(smbios_pinfo_archid, sizeof (smbios_pinfo_archid));
+ bcopy(smbios_pinfo_archid, rv.smbpairv_archid,
+ sizeof (smbios_pinfo_archid));
+ arc4random_buf(smbios_pinfo_machid, sizeof (smbios_pinfo_machid));
+ bcopy(smbios_pinfo_machid, rv.smbpairv_machid,
+ sizeof (smbios_pinfo_machid));
+ rv.smbpairv_boot = 1;
+ rv.smbpairv_isa = htole64(smbios_pinfo_isa);
+ rv.smbpairv_privlvl = SMB_RV_PRIV_M | SMB_RV_PRIV_S;
+ arc4random_buf(smbios_pinfo_metdi, sizeof (smbios_pinfo_metdi));
+ bcopy(smbios_pinfo_metdi, rv.smbpairv_metdi,
+ sizeof (smbios_pinfo_metdi));
+ arc4random_buf(smbios_pinfo_mitdi, sizeof (smbios_pinfo_mitdi));
+ bcopy(smbios_pinfo_mitdi, rv.smbpairv_mitdi,
+ sizeof (smbios_pinfo_mitdi));
+ rv.smbpairv_xlen = SMB_RV_WIDTH_64B;
+ rv.smbpairv_mxlen = SMB_RV_WIDTH_64B;
+ rv.smbpairv_sxlen = SMB_RV_WIDTH_128B;
+ rv.smbpairv_uxlen = SMB_RV_WIDTH_32B;
+
+ smbios_test_table_append_raw(table, &rv, sizeof (rv));
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+static void
+smbios_test_pinfo_id_mismatch(uint8_t *act, uint8_t *exp)
+{
+ uint_t i;
+ (void) fprintf(stderr, "found: ");
+ for (i = 0; i < 16; i++) {
+ (void) fprintf(stderr, " %02x", act[i]);
+ }
+ (void) fprintf(stderr, "\nexpected: ");
+ for (i = 0; i < 16; i++) {
+ (void) fprintf(stderr, " %02x", exp[i]);
+ }
+ (void) fprintf(stderr, "\n");
+}
+
+boolean_t
+smbios_test_pinfo_verify_riscv(smbios_hdl_t *hdl)
+{
+ smbios_struct_t sp;
+ smbios_processor_info_t pinfo;
+ smbios_processor_info_riscv_t rv;
+ boolean_t ret = B_TRUE;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_PROCESSOR_INFO, &sp) == -1) {
+ warnx("failed to lookup SMBIOS processor additional "
+ "information: %s", smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (smbios_info_processor_info(hdl, sp.smbstr_id, &pinfo) != 0) {
+ warnx("failed to get SMBIOS processor additional "
+ "information: %s", smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (pinfo.smbpi_processor != smbios_pinfo_phandle) {
+ warnx("processor handle incorrect, found 0x%x",
+ pinfo.smbpi_processor);
+ ret = B_FALSE;
+ }
+
+ if (pinfo.smbpi_ptype != SMB_PROCINFO_T_RV64) {
+ warnx("processor type incorrect, found 0x%x",
+ pinfo.smbpi_ptype);
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_processor_info_type_desc(pinfo.smbpi_ptype),
+ "64-bit RISC-V (RV64)") != 0) {
+ warnx("processor type incorrect, found %s",
+ smbios_processor_info_type_desc(pinfo.smbpi_ptype));
+ ret = B_FALSE;
+ }
+
+ if (smbios_info_processor_riscv(hdl, sp.smbstr_id, &rv) != 0) {
+
+ warnx("failed to get SMBIOS processor additional "
+ "information for RISC-V: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (bcmp(rv.smbpirv_hartid, smbios_pinfo_hartid,
+ sizeof (smbios_pinfo_hartid)) != 0) {
+ warnx("RISC-V hart id's don't match");
+ smbios_test_pinfo_id_mismatch(rv.smbpirv_hartid,
+ smbios_pinfo_hartid);
+ ret = B_FALSE;
+ }
+
+ if (bcmp(rv.smbpirv_vendid, smbios_pinfo_vendid,
+ sizeof (smbios_pinfo_vendid)) != 0) {
+ warnx("RISC-V vend id's don't match");
+ smbios_test_pinfo_id_mismatch(rv.smbpirv_vendid,
+ smbios_pinfo_vendid);
+ ret = B_FALSE;
+ }
+
+ if (bcmp(rv.smbpirv_archid, smbios_pinfo_archid,
+ sizeof (smbios_pinfo_archid)) != 0) {
+ warnx("RISC-V arch id's don't match");
+ smbios_test_pinfo_id_mismatch(rv.smbpirv_archid,
+ smbios_pinfo_archid);
+ ret = B_FALSE;
+ }
+
+ if (bcmp(rv.smbpirv_machid, smbios_pinfo_machid,
+ sizeof (smbios_pinfo_machid)) != 0) {
+ warnx("RISC-V mach id's don't match");
+ smbios_test_pinfo_id_mismatch(rv.smbpirv_machid,
+ smbios_pinfo_machid);
+ ret = B_FALSE;
+ }
+
+ if (bcmp(rv.smbpirv_metdi, smbios_pinfo_metdi,
+ sizeof (smbios_pinfo_metdi)) != 0) {
+ warnx("RISC-V METDI don't match");
+ smbios_test_pinfo_id_mismatch(rv.smbpirv_metdi,
+ smbios_pinfo_metdi);
+ ret = B_FALSE;
+ }
+
+ if (bcmp(rv.smbpirv_mitdi, smbios_pinfo_mitdi,
+ sizeof (smbios_pinfo_mitdi)) != 0) {
+ warnx("RISC-V METDI don't match");
+ smbios_test_pinfo_id_mismatch(rv.smbpirv_mitdi,
+ smbios_pinfo_mitdi);
+ ret = B_FALSE;
+ }
+
+ if (rv.smbpirv_isa != smbios_pinfo_isa) {
+ warnx("RISC-V ISA mismatch");
+ ret = B_FALSE;
+ }
+
+ if (rv.smbpirv_privlvl != (SMB_RV_PRIV_M | SMB_RV_PRIV_S)) {
+ warnx("RISC-V privilege level mismatch, found: 0x%x",
+ rv.smbpirv_privlvl);
+ ret = B_FALSE;
+ }
+
+ if (rv.smbpirv_xlen != SMB_RV_WIDTH_64B) {
+ warnx("RISC-V xlen mismatch, found: 0x%x", rv.smbpirv_xlen);
+ ret = B_FALSE;
+ }
+
+ if (rv.smbpirv_mxlen != SMB_RV_WIDTH_64B) {
+ warnx("RISC-V mxlen mismatch, found: 0x%x", rv.smbpirv_mxlen);
+ ret = B_FALSE;
+ }
+
+ if (rv.smbpirv_sxlen != SMB_RV_WIDTH_128B) {
+ warnx("RISC-V sxlen mismatch, found: 0x%x", rv.smbpirv_sxlen);
+ ret = B_FALSE;
+ }
+
+ if (rv.smbpirv_uxlen != SMB_RV_WIDTH_32B) {
+ warnx("RISC-V uxlen mismatch, found: 0x%x", rv.smbpirv_uxlen);
+ ret = B_FALSE;
+ }
+
+ /*
+ * Finally, use this to spot check several of the different RISC-V
+ * strings.
+ */
+ if (strcmp(smbios_riscv_priv_desc(SMB_RV_PRIV_M), "Machine Mode") !=
+ 0) {
+ warnx("SMB_RV_PRIV_M string desc mismatch, found %s",
+ smbios_riscv_priv_desc(SMB_RV_PRIV_M));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_priv_name(SMB_RV_PRIV_U), "SMB_RV_PRIV_U") !=
+ 0) {
+ warnx("SMB_RV_PRIV_U string name mismatch, found %s",
+ smbios_riscv_priv_name(SMB_RV_PRIV_U));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_width_desc(SMB_RV_WIDTH_64B), "64-bit") !=
+ 0) {
+ warnx("SMB_RV_WIDTH_64B string desc mismatch, found %s",
+ smbios_riscv_width_desc(SMB_RV_WIDTH_64B));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_width_desc(SMB_RV_WIDTH_128B), "128-bit") !=
+ 0) {
+ warnx("SMB_RV_WIDTH_128B string desc mismatch, found %s",
+ smbios_riscv_width_desc(SMB_RV_WIDTH_128B));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_isa_desc(SMB_RV_ISA_A), "Atomic") != 0) {
+ warnx("SMB_RV_ISA_A string desc mismatch, found %s",
+ smbios_riscv_isa_desc(SMB_RV_ISA_A));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_isa_desc(SMB_RV_ISA_C), "Compressed") != 0) {
+ warnx("SMB_RV_ISA_Q string desc mismatch, found %s",
+ smbios_riscv_isa_desc(SMB_RV_ISA_C));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_isa_desc(SMB_RV_ISA_Q),
+ "Quad-precision floating-poit") != 0) {
+ warnx("SMB_RV_ISA_Q string desc mismatch, found %s",
+ smbios_riscv_isa_desc(SMB_RV_ISA_Q));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_isa_name(SMB_RV_ISA_A), "SMB_RV_ISA_A") != 0) {
+ warnx("SMB_RV_ISA_A string name mismatch, found %s",
+ smbios_riscv_isa_name(SMB_RV_ISA_A));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_isa_name(SMB_RV_ISA_C), "SMB_RV_ISA_C") != 0) {
+ warnx("SMB_RV_ISA_Q string name mismatch, found %s",
+ smbios_riscv_isa_name(SMB_RV_ISA_C));
+ ret = B_FALSE;
+ }
+
+ if (strcmp(smbios_riscv_isa_name(SMB_RV_ISA_Q), "SMB_RV_ISA_Q") != 0) {
+ warnx("SMB_RV_ISA_Q string name mismatch, found %s",
+ smbios_riscv_isa_name(SMB_RV_ISA_Q));
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+/*
+ * This shows having an invalid table length.
+ */
+boolean_t
+smbios_test_pinfo_mktable_invlen1(smbios_test_table_t *table)
+{
+ smb_processor_info_t pi;
+
+ pi.smbpai_hdr.smbh_type = SMB_TYPE_PROCESSOR_INFO;
+ pi.smbpai_hdr.smbh_len = 2;
+ pi.smbpai_proc = htole16(smbios_pinfo_phandle);
+ pi.smbpai_len = 0;
+ pi.smbpai_type = SMB_PROCINFO_T_AMD64;
+
+ (void) smbios_test_table_append(table, &pi, sizeof (pi));
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+/*
+ * This sets the internal length of the additional processor information data to
+ * go beyond the length of the basic structure.
+ */
+boolean_t
+smbios_test_pinfo_mktable_invlen2(smbios_test_table_t *table)
+{
+ smb_processor_info_t pi;
+ smb_processor_info_riscv_t rv;
+
+ pi.smbpai_hdr.smbh_type = SMB_TYPE_PROCESSOR_INFO;
+ pi.smbpai_hdr.smbh_len = sizeof (smb_processor_info_t);
+ pi.smbpai_proc = htole16(smbios_pinfo_phandle);
+ pi.smbpai_len = sizeof (smb_processor_info_riscv_t);
+ pi.smbpai_type = SMB_PROCINFO_T_RV64;
+
+ (void) smbios_test_table_append(table, &pi, sizeof (pi));
+
+ arc4random_buf(&rv, sizeof (rv));
+ rv.smbpairv_vers = 1;
+ rv.smbpairv_len = sizeof (smb_processor_info_riscv_t);
+
+ smbios_test_table_append_raw(table, &rv, sizeof (rv));
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+/*
+ * This verifies that we can detect a header length that doesn't properly
+ * contain both the risc-v and base structure.
+ */
+boolean_t
+smbios_test_pinfo_mktable_invlen3(smbios_test_table_t *table)
+{
+ smb_processor_info_t pi;
+ smb_processor_info_riscv_t rv;
+
+ pi.smbpai_hdr.smbh_type = SMB_TYPE_PROCESSOR_INFO;
+ pi.smbpai_hdr.smbh_len = sizeof (smb_processor_info_t);
+ pi.smbpai_proc = htole16(smbios_pinfo_phandle);
+ pi.smbpai_len = 0;
+ pi.smbpai_type = SMB_PROCINFO_T_RV64;
+
+ (void) smbios_test_table_append(table, &pi, sizeof (pi));
+
+ arc4random_buf(&rv, sizeof (rv));
+ rv.smbpairv_vers = 1;
+ rv.smbpairv_len = sizeof (smb_processor_info_riscv_t);
+
+ smbios_test_table_append_raw(table, &rv, sizeof (rv));
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+/*
+ * This verifies that we can detect an inner risc-v additional processor
+ * information section that declares its size to be beyond the header of the
+ * structure.
+ */
+boolean_t
+smbios_test_pinfo_mktable_invlen4(smbios_test_table_t *table)
+{
+ smb_processor_info_t pi;
+ smb_processor_info_riscv_t rv;
+
+ pi.smbpai_hdr.smbh_type = SMB_TYPE_PROCESSOR_INFO;
+ pi.smbpai_hdr.smbh_len = sizeof (smb_processor_info_t) +
+ sizeof (smb_processor_info_riscv_t);
+ pi.smbpai_proc = htole16(smbios_pinfo_phandle);
+ pi.smbpai_len = sizeof (smb_processor_info_riscv_t);
+ pi.smbpai_type = SMB_PROCINFO_T_RV64;
+
+ (void) smbios_test_table_append(table, &pi, sizeof (pi));
+
+ arc4random_buf(&rv, sizeof (rv));
+ rv.smbpairv_vers = 1;
+ rv.smbpairv_len = sizeof (smb_processor_info_riscv_t) * 2;
+
+ smbios_test_table_append_raw(table, &rv, sizeof (rv));
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+static boolean_t
+smbios_test_pinfo_verify_badtable(smbios_hdl_t *hdl, int smberr,
+ boolean_t valid_pinfo)
+{
+ smbios_struct_t sp;
+ smbios_processor_info_t pinfo;
+ smbios_processor_info_riscv_t rv;
+ boolean_t ret = B_TRUE;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_PROCESSOR_INFO, &sp) == -1) {
+ warnx("failed to lookup SMBIOS processor additional "
+ "information: %s", smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (!valid_pinfo) {
+ if (smbios_info_processor_info(hdl, sp.smbstr_id, &pinfo) !=
+ -1) {
+ warnx("accidentally parsed invalid processor "
+ "additional information as valid");
+ ret = B_FALSE;
+ }
+
+ if (smbios_errno(hdl) != smberr) {
+ warnx("encountered wrong error for processor info, "
+ "found: 0x%x", smbios_errno(hdl));
+ ret = B_FALSE;
+ }
+ } else {
+ if (smbios_info_processor_info(hdl, sp.smbstr_id, &pinfo) !=
+ 0) {
+ warnx("failed to get SMBIOS processor additional "
+ "information: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ ret = B_FALSE;
+ }
+ }
+
+ if (smbios_info_processor_riscv(hdl, sp.smbstr_id, &rv) != -1) {
+ warnx("accidentally got riscv info on invalid handle");
+ ret = B_FALSE;
+ }
+
+ if (smbios_errno(hdl) != smberr) {
+ warnx("encountered wrong error for amd64 info, found: 0x%x",
+ smbios_errno(hdl));
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+boolean_t
+smbios_test_pinfo_verify_invlen1(smbios_hdl_t *hdl)
+{
+ return (smbios_test_pinfo_verify_badtable(hdl, ESMB_SHORT, B_FALSE));
+}
+
+boolean_t
+smbios_test_pinfo_verify_invlen2(smbios_hdl_t *hdl)
+{
+ return (smbios_test_pinfo_verify_badtable(hdl, ESMB_CORRUPT, B_FALSE));
+}
+
+boolean_t
+smbios_test_pinfo_verify_invlen3(smbios_hdl_t *hdl)
+{
+ return (smbios_test_pinfo_verify_badtable(hdl, ESMB_SHORT, B_TRUE));
+}
+
+boolean_t
+smbios_test_pinfo_verify_invlen4(smbios_hdl_t *hdl)
+{
+ return (smbios_test_pinfo_verify_badtable(hdl, ESMB_CORRUPT, B_TRUE));
+}
+
+boolean_t
+smbios_test_pinfo_verify_badtype(smbios_hdl_t *hdl)
+{
+ smbios_struct_t sp;
+ smbios_processor_info_t pinfo;
+ smbios_processor_info_riscv_t rv;
+ boolean_t ret = B_TRUE;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
+ warnx("failed to lookup SMBIOS memory device information: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (smbios_info_processor_info(hdl, sp.smbstr_id, &pinfo) != -1) {
+ warnx("accidentally parsed invalid processor additional "
+ "information as valid");
+ ret = B_FALSE;
+ }
+
+ if (smbios_errno(hdl) != ESMB_TYPE) {
+ warnx("encountered wrong error for processor info, found: 0x%x",
+ smbios_errno(hdl));
+ ret = B_FALSE;
+ }
+
+ if (smbios_info_processor_riscv(hdl, sp.smbstr_id, &rv) != -1) {
+ warnx("accidentally got riscv info on invalid handle");
+ ret = B_FALSE;
+ }
+
+ if (smbios_errno(hdl) != ESMB_TYPE) {
+ warnx("encountered wrong error for processor info, found: 0x%x",
+ smbios_errno(hdl));
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
diff --git a/usr/src/test/util-tests/tests/smbios/smbios_test_slot.c b/usr/src/test/util-tests/tests/smbios/smbios_test_slot.c
new file mode 100644
index 0000000000..d84cc10e91
--- /dev/null
+++ b/usr/src/test/util-tests/tests/smbios/smbios_test_slot.c
@@ -0,0 +1,188 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2018, Joyent, Inc.
+ */
+
+/*
+ * Basic testing of the SMBIOS 3.2 Slot extensions.
+ */
+
+#include "smbios_test.h"
+
+static const char *smbios_test_name = "The One Slot";
+
+boolean_t
+smbios_test_slot_mktable(smbios_test_table_t *table)
+{
+ smb_slot_t slot;
+ smb_slot_peer_t peers[2];
+ const uint8_t endstring = 0;
+
+ slot.smbsl_hdr.smbh_type = SMB_TYPE_SLOT;
+ slot.smbsl_hdr.smbh_len = sizeof (smb_slot_t) + sizeof (peers);
+
+ slot.smbsl_name = 1;
+ slot.smbsl_type = SMB_SLT_PCIE3G16;
+ slot.smbsl_width = SMB_SLW_16X;
+ slot.smbsl_length = SMB_SLL_SHORT;
+ slot.smbsl_id = htole16(1);
+ slot.smbsl_ch1 = SMB_SLCH1_33V;
+ slot.smbsl_ch2 = SMB_SLCH2_PME;
+ slot.smbsl_sg = htole16(1);
+ slot.smbsl_bus = 0x42;
+ slot.smbsl_df = 0x23;
+ slot.smbsl_dbw = SMB_SLW_16X;
+ slot.smbsl_npeers = 2;
+ peers[0].smbspb_group_no = htole16(1);
+ peers[0].smbspb_bus = 0x42;
+ peers[0].smbspb_df = 0x42;
+ peers[0].smbspb_width = SMB_SLW_8X;
+
+ peers[1].smbspb_group_no = htole16(1);
+ peers[1].smbspb_bus = 0x23;
+ peers[1].smbspb_df = 0x31;
+ peers[1].smbspb_width = SMB_SLW_8X;
+
+ (void) smbios_test_table_append(table, &slot, sizeof (slot));
+ (void) smbios_test_table_append_raw(table, peers, sizeof (peers));
+ (void) smbios_test_table_append_string(table, smbios_test_name);
+ (void) smbios_test_table_append_raw(table, &endstring,
+ sizeof (endstring));
+
+ smbios_test_table_append_eot(table);
+
+ return (B_TRUE);
+}
+
+boolean_t
+smbios_test_slot_verify(smbios_hdl_t *hdl)
+{
+ smbios_struct_t sp;
+ smbios_slot_t slot;
+ uint_t npeers;
+ smbios_slot_peer_t *peers;
+ uint_t errs = 0;
+
+ if (smbios_lookup_type(hdl, SMB_TYPE_SLOT, &sp) == -1) {
+ warnx("failed to lookup SMBIOS slot: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (smbios_info_slot(hdl, sp.smbstr_id, &slot) != 0) {
+ warnx("failed to get SMBIOS slot info: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ /*
+ * Verify everything we'd expect about the slot.
+ */
+ if (strcmp(slot.smbl_name, smbios_test_name) != 0) {
+ warnx("slot name mismatch, expected %s, found %s",
+ smbios_test_name, slot.smbl_name);
+ errs++;
+ }
+
+ if (slot.smbl_type != SMB_SLT_PCIE3G16) {
+ warnx("incorrect slot type, found %u", slot.smbl_type);
+ errs++;
+ }
+
+ if (slot.smbl_width != SMB_SLW_16X) {
+ warnx("incorrect slot width, found %u", slot.smbl_width);
+ errs++;
+ }
+
+ if (slot.smbl_length != SMB_SLL_SHORT) {
+ warnx("incorrect slot length, found %u", slot.smbl_length);
+ errs++;
+ }
+
+ if (slot.smbl_dbw != SMB_SLW_16X) {
+ warnx("incorrect slot data bus width, found %u", slot.smbl_dbw);
+ errs++;
+ }
+
+ if (slot.smbl_npeers != 2) {
+ warnx("incorrect number of slot peers, found %u",
+ slot.smbl_npeers);
+ errs++;
+ }
+
+ if (smbios_info_slot_peers(hdl, sp.smbstr_id, &npeers, &peers) != 0) {
+ warnx("failed to get SMBIOS peer info: %s",
+ smbios_errmsg(smbios_errno(hdl)));
+ return (B_FALSE);
+ }
+
+ if (npeers != 2) {
+ warnx("got wrong number of slot peers: %u\n",
+ npeers);
+ return (B_FALSE);
+ }
+
+ if (peers[0].smblp_group != 1) {
+ warnx("incorrect group for peer 0: %u", peers[0].smblp_group);
+ errs++;
+ }
+
+ if (peers[0].smblp_data_width != SMB_SLW_8X) {
+ warnx("incorrect data width for peer 0: %u",
+ peers[0].smblp_data_width);
+ errs++;
+ }
+
+ if (peers[0].smblp_device != (0x42 >> 3)) {
+ warnx("incorrect PCI device for peer 0: %u",
+ peers[0].smblp_device);
+ errs++;
+ }
+
+ if (peers[0].smblp_function != (0x42 & 0x7)) {
+ warnx("incorrect PCI function for peer 0: %u",
+ peers[0].smblp_function);
+ errs++;
+ }
+
+ if (peers[1].smblp_group != 1) {
+ warnx("incorrect group for peer 1: %u", peers[1].smblp_group);
+ errs++;
+ }
+
+ if (peers[1].smblp_device != (0x31 >> 3)) {
+ warnx("incorrect PCI device for peer 1: %u",
+ peers[1].smblp_device);
+ errs++;
+ }
+
+ if (peers[1].smblp_function != (0x31 & 0x7)) {
+ warnx("incorrect PCI function for peer 1: %u",
+ peers[1].smblp_function);
+ errs++;
+ }
+
+ if (peers[1].smblp_data_width != SMB_SLW_8X) {
+ warnx("incorrect data width for peer 1: %u",
+ peers[1].smblp_data_width);
+ errs++;
+ }
+
+ smbios_info_slot_peers_free(hdl, npeers, peers);
+
+ if (errs > 0) {
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
diff --git a/usr/src/tools/make/Makefile.com b/usr/src/tools/make/Makefile.com
index 97c32780bf..34074bb00a 100644
--- a/usr/src/tools/make/Makefile.com
+++ b/usr/src/tools/make/Makefile.com
@@ -17,5 +17,5 @@ CC = $(NATIVECC)
CCC = $(NATIVECCC)
CFLAGS = $(NATIVE_CFLAGS)
CFLAGS += $(CCVERBOSE)
-CCFLAGS = $(NATIVE_CCFLAGS)
+CCFLAGS = $(NATIVE_CCFLAGS) -_gcc4=-std=gnu++0x
CPPFLAGS = -I$(MAKE_INCLUDE) $(MAKE_DEFS)
diff --git a/usr/src/tools/scripts/git-pbchk.py b/usr/src/tools/scripts/git-pbchk.py
index 99bce29087..ec01255c6e 100644
--- a/usr/src/tools/scripts/git-pbchk.py
+++ b/usr/src/tools/scripts/git-pbchk.py
@@ -21,7 +21,7 @@
# Copyright (c) 2015, 2016 by Delphix. All rights reserved.
# Copyright 2016 Nexenta Systems, Inc.
# Copyright (c) 2019, Joyent, Inc.
-# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
#
from __future__ import print_function
@@ -280,7 +280,7 @@ def jstyle(root, parent, flist, output):
ret = 0
output.write("Java style:\n")
for f in flist(lambda x: x.endswith('.java')):
- with io.open(f, encoding='utf-8', errors='replace') as fh:
+ with io.open(f, mode='rb') as fh:
ret |= JStyle.jstyle(fh, output=output, picky=True)
return ret
diff --git a/usr/src/tools/smatch/Makefile b/usr/src/tools/smatch/Makefile
index c2882381bd..a66a0af838 100644
--- a/usr/src/tools/smatch/Makefile
+++ b/usr/src/tools/smatch/Makefile
@@ -8,7 +8,7 @@
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
-# Copyright 2019 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
#
@@ -20,7 +20,7 @@
#
PROG = smatch
-SPARSE_VERSION = 0.6.1-rc1-il-2
+SPARSE_VERSION = 0.6.1-rc1-il-3
include ../Makefile.tools
@@ -31,7 +31,8 @@ sparc_CC = $(GNUC_ROOT)/bin/gcc
i386_SMATCHFLAGS = -msave-args
sparc_SMATCHFLAGS =
-CFLAGS = -O -m64 $($(MACH)_SMATCHFLAGS) -D__sun -Wall -Wno-unknown-pragmas -std=gnu99 -nodefaultlibs
+CFLAGS = -O -m64 -std=gnu99 -nodefaultlibs -D__sun $($(MACH)_SMATCHFLAGS)
+CFLAGS += -Wall -Wno-unknown-pragmas -Wno-psabi
SMATCHDATADIR = $(ROOTONBLDSHARE)/smatch
@@ -185,6 +186,7 @@ SMATCH_OBJS += smatch_type_val.o
SMATCH_OBJS += smatch_unknown_value.o
SMATCH_OBJS += smatch_untracked_param.o
SMATCH_OBJS += smatch_var_sym.o
+SMATCH_OBJS += smatch_parsed_conditions.o
SMATCH_CHECK_OBJS:sh=ls src/check_*.c | sed -e 's+\.c+.o+;s+src/++;'
diff --git a/usr/src/tools/smatch/src/Makefile b/usr/src/tools/smatch/src/Makefile
index 164fa1bd33..e9fb252972 100644
--- a/usr/src/tools/smatch/src/Makefile
+++ b/usr/src/tools/smatch/src/Makefile
@@ -1,4 +1,4 @@
-VERSION=0.6.1-rc1-il-2
+VERSION=0.6.1-rc1-il-3
########################################################################
# The following variables can be overwritten from the command line
@@ -95,7 +95,7 @@ all:
# common flags/options/...
cflags = -fno-strict-aliasing
-cflags += -Wall -Wwrite-strings -Wno-switch
+cflags += -Wall -Wwrite-strings -Wno-switch -Wno-psabi
GCC_BASE := $(shell $(CC) --print-file-name=)
cflags += -DGCC_BASE=\"$(GCC_BASE)\"
@@ -254,6 +254,7 @@ SMATCH_OBJS += smatch_param_set.o
SMATCH_OBJS += smatch_param_to_mtag_data.o
SMATCH_OBJS += smatch_param_used.o
SMATCH_OBJS += smatch_parse_call_math.o
+SMATCH_OBJS += smatch_parsed_conditions.o
SMATCH_OBJS += smatch_passes_array_size.o
SMATCH_OBJS += smatch_project.o
SMATCH_OBJS += smatch_ranges.o
diff --git a/usr/src/tools/smatch/src/check_debug.c b/usr/src/tools/smatch/src/check_debug.c
index 8e328e8c1b..abe98b72db 100644
--- a/usr/src/tools/smatch/src/check_debug.c
+++ b/usr/src/tools/smatch/src/check_debug.c
@@ -15,6 +15,8 @@
* along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
*/
+#include <string.h>
+
#include "smatch.h"
#include "smatch_slist.h"
#include "smatch_extra.h"
@@ -86,7 +88,7 @@ static void match_states(const char *fn, struct expression *expr, void *info)
}
FOR_EACH_SM(__get_cur_stree(), sm) {
- if (strcmp(check_name(sm->owner), check_arg->string->data) != 0)
+ if (!strstr(check_name(sm->owner), check_arg->string->data))
continue;
sm_msg("%s", show_sm(sm));
found = 1;
@@ -514,6 +516,16 @@ static void match_local_debug_off(const char *fn, struct expression *expr, void
local_debug = 0;
}
+static void match_debug_db_on(const char *fn, struct expression *expr, void *info)
+{
+ debug_db = 1;
+}
+
+static void match_debug_db_off(const char *fn, struct expression *expr, void *info)
+{
+ debug_db = 0;
+}
+
static void match_about(const char *fn, struct expression *expr, void *info)
{
struct expression *arg;
@@ -773,6 +785,8 @@ void check_debug(int id)
add_function_hook("__smatch_debug_off", &match_debug_off, NULL);
add_function_hook("__smatch_local_debug_on", &match_local_debug_on, NULL);
add_function_hook("__smatch_local_debug_off", &match_local_debug_off, NULL);
+ add_function_hook("__smatch_debug_db_on", &match_debug_db_on, NULL);
+ add_function_hook("__smatch_debug_db_off", &match_debug_db_off, NULL);
add_function_hook("__smatch_intersection", &match_intersection, NULL);
add_function_hook("__smatch_type", &match_type, NULL);
add_implied_return_hook("__smatch_type_rl_helper", &match_type_rl_return, NULL);
diff --git a/usr/src/tools/smatch/src/check_debug.h b/usr/src/tools/smatch/src/check_debug.h
index 52a71a619b..823fca2003 100644
--- a/usr/src/tools/smatch/src/check_debug.h
+++ b/usr/src/tools/smatch/src/check_debug.h
@@ -46,6 +46,9 @@ static inline void __smatch_debug_off(void){}
static inline void __smatch_local_debug_on(void){}
static inline void __smatch_local_debug_off(void){}
+static inline void __smatch_debug_db_on(void){}
+static inline void __smatch_debug_db_off(void){}
+
static inline void __smatch_debug_implied_on(void){}
static inline void __smatch_debug_implied_off(void){}
diff --git a/usr/src/tools/smatch/src/check_frees_param.c b/usr/src/tools/smatch/src/check_frees_param.c
index 712f1ca7d0..e05e7f5568 100644
--- a/usr/src/tools/smatch/src/check_frees_param.c
+++ b/usr/src/tools/smatch/src/check_frees_param.c
@@ -29,27 +29,12 @@ static int my_id;
STATE(freed);
STATE(ignore);
-STATE(param);
static void set_ignore(struct sm_state *sm, struct expression *mod_expr)
{
set_state(my_id, sm->name, sm->sym, &ignore);
}
-static void match_function_def(struct symbol *sym)
-{
- struct symbol *arg;
- int i;
-
- i = -1;
- FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
- i++;
- if (!arg->ident)
- continue;
- set_state(my_id, arg->ident->name, arg, &param);
- } END_FOR_EACH_PTR(arg);
-}
-
static void freed_variable(struct expression *expr)
{
struct sm_state *sm;
@@ -111,8 +96,6 @@ void check_frees_param(int id)
return;
}
- add_hook(&match_function_def, FUNC_DEF_HOOK);
-
add_function_hook("free", &match_free, INT_PTR(0));
select_return_implies_hook(PARAM_FREED, &set_param_freed);
diff --git a/usr/src/tools/smatch/src/check_list.h b/usr/src/tools/smatch/src/check_list.h
index 4a9c069a0e..b687018e48 100644
--- a/usr/src/tools/smatch/src/check_list.h
+++ b/usr/src/tools/smatch/src/check_list.h
@@ -25,16 +25,16 @@ CK(register_param_compare_limit)
CK(register_param_compare_limit_links)
CK(register_returns_early)
+CK(register_param_cleared) /* param_set relies on param_cleared */
+CK(register_param_set)
+
CK(register_smatch_ignore)
CK(register_buf_size)
CK(register_strlen)
CK(register_strlen_equiv)
CK(register_capped)
CK(register_parse_call_math)
-CK(register_param_limit)
CK(register_param_filter)
-CK(register_param_set)
-CK(register_param_cleared)
CK(register_struct_assignment)
CK(register_comparison)
CK(register_comparison_links)
@@ -58,6 +58,7 @@ CK(register_imaginary_absolute)
CK(register_bits)
CK(register_fn_arg_link)
CK(register_parameter_names)
+CK(register_param_limit)
CK(register_return_to_param)
CK(register_return_to_param_links)
CK(register_constraints)
@@ -207,6 +208,7 @@ CK(check_cmn_err)
CK(register_scope)
CK(register_stored_conditions)
CK(register_stored_conditions_links)
+CK(register_parsed_conditions)
CK(register_sval)
CK(register_buf_size_late)
CK(register_smatch_extra_late)
diff --git a/usr/src/tools/smatch/src/check_locking.c b/usr/src/tools/smatch/src/check_locking.c
index f0b2ed4d46..285a52cedf 100644
--- a/usr/src/tools/smatch/src/check_locking.c
+++ b/usr/src/tools/smatch/src/check_locking.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2009 Dan Carpenter.
+ * Copyright (C) 2019 Oracle.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -15,17 +16,7 @@
* along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
*/
-/*
- * This test checks that locks are held the same across all returns.
- *
- * Of course, some functions are designed to only hold the locks on success.
- * Oh well... We can rewrite it later if we want.
- *
- * The list of wine locking functions came from an earlier script written
- * by Michael Stefaniuc.
- *
- */
-
+#include <ctype.h>
#include "parse.h"
#include "smatch.h"
#include "smatch_extra.h"
@@ -33,25 +24,55 @@
static int my_id;
-static int func_has_transition;
-
STATE(locked);
+STATE(half_locked);
STATE(start_state);
STATE(unlocked);
STATE(impossible);
+STATE(restore);
enum action {
LOCK,
UNLOCK,
+ RESTORE,
+};
+
+enum lock_type {
+ spin_lock,
+ read_lock,
+ write_lock,
+ mutex,
+ bottom_half,
+ irq,
+ sem,
+ prepare_lock,
+ enable_lock,
};
+const char *get_lock_name(enum lock_type type)
+{
+ static const char *names[] = {
+ [spin_lock] = "spin_lock",
+ [read_lock] = "read_lock",
+ [write_lock] = "write_lock",
+ [mutex] = "mutex",
+ [bottom_half] = "bottom_half",
+ [irq] = "irq",
+ [sem] = "sem",
+ [prepare_lock] = "prepare_lock",
+ [enable_lock] = "enable_lock",
+ };
+
+ return names[type];
+}
+
enum return_type {
ret_any,
- ret_non_zero,
ret_zero,
ret_one,
ret_negative,
ret_positive,
+ ret_valid_ptr,
};
#define RETURN_VAL -1
@@ -60,338 +81,361 @@ enum return_type {
struct lock_info {
const char *function;
enum action action;
- const char *name;
+ enum lock_type type;
int arg;
enum return_type return_type;
};
-static struct lock_info wine_lock_table[] = {
- {"create_window_handle", LOCK, "create_window_handle", RETURN_VAL, ret_non_zero},
- {"WIN_GetPtr", LOCK, "create_window_handle", RETURN_VAL, ret_non_zero},
- {"WIN_ReleasePtr", UNLOCK, "create_window_handle", 0, ret_any},
- {"EnterCriticalSection", LOCK, "CriticalSection", 0, ret_any},
- {"LeaveCriticalSection", UNLOCK, "CriticalSection", 0, ret_any},
- {"RtlEnterCriticalSection", LOCK, "RtlCriticalSection", 0, ret_any},
- {"RtlLeaveCriticalSection", UNLOCK, "RtlCriticalSection", 0, ret_any},
- {"GDI_GetObjPtr", LOCK, "GDI_Get", 0, ret_non_zero},
- {"GDI_ReleaseObj", UNLOCK, "GDI_Get", 0, ret_any},
- {"LdrLockLoaderLock", LOCK, "LdrLockLoaderLock", 2, ret_any},
- {"LdrUnlockLoaderLock", UNLOCK, "LdrLockLoaderLock", 1, ret_any},
- {"_lock", LOCK, "_lock", 0, ret_any},
- {"_unlock", UNLOCK, "_lock", 0, ret_any},
- {"msiobj_lock", LOCK, "msiobj_lock", 0, ret_any},
- {"msiobj_unlock", UNLOCK, "msiobj_lock", 0, ret_any},
- {"RtlAcquirePebLock", LOCK, "PebLock", NO_ARG, ret_any},
- {"RtlReleasePebLock", UNLOCK, "PebLock", NO_ARG, ret_any},
- {"server_enter_uninterrupted_section", LOCK, "server_uninterrupted_section", 0, ret_any},
- {"server_leave_uninterrupted_section", UNLOCK, "server_uninterrupted_section", 0, ret_any},
- {"RtlLockHeap", LOCK, "RtlLockHeap", 0, ret_any},
- {"RtlUnlockHeap", UNLOCK, "RtlLockHeap", 0, ret_any},
- {"_EnterSysLevel", LOCK, "SysLevel", 0, ret_any},
- {"_LeaveSysLevel", UNLOCK, "SysLevel", 0, ret_any},
- {"USER_Lock", LOCK, "USER_Lock", NO_ARG, ret_any},
- {"USER_Unlock", UNLOCK, "USER_Lock", NO_ARG, ret_any},
- {"wine_tsx11_lock", LOCK, "wine_tsx11_lock", NO_ARG, ret_any},
- {"wine_tsx11_unlock", UNLOCK, "wine_tsx11_lock", NO_ARG, ret_any},
- {"wine_tsx11_lock_ptr", LOCK, "wine_tsx11_lock_ptr", NO_ARG, ret_any},
- {"wine_tsx11_unlock_ptr", UNLOCK, "wine_tsx11_lock_ptr", NO_ARG, ret_any},
- {"wined3d_mutex_lock", LOCK, "wined3d_mutex_lock", NO_ARG, ret_any},
- {"wined3d_mutex_unlock", UNLOCK, "wined3d_mutex_lock", NO_ARG, ret_any},
- {"X11DRV_DIB_Lock", LOCK, "X11DRV_DIB_Lock", 0, ret_any},
- {"X11DRV_DIB_Unlock", UNLOCK, "X11DRV_DIB_Lock", 0, ret_any},
+static struct lock_info lock_table[] = {
+ {"spin_lock", LOCK, spin_lock, 0, ret_any},
+ {"spin_unlock", UNLOCK, spin_lock, 0, ret_any},
+ {"spin_lock_nested", LOCK, spin_lock, 0, ret_any},
+ {"_spin_lock", LOCK, spin_lock, 0, ret_any},
+ {"_spin_unlock", UNLOCK, spin_lock, 0, ret_any},
+ {"_spin_lock_nested", LOCK, spin_lock, 0, ret_any},
+ {"__spin_lock", LOCK, spin_lock, 0, ret_any},
+ {"__spin_unlock", UNLOCK, spin_lock, 0, ret_any},
+ {"__spin_lock_nested", LOCK, spin_lock, 0, ret_any},
+ {"raw_spin_lock", LOCK, spin_lock, 0, ret_any},
+ {"raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any},
+ {"_raw_spin_lock", LOCK, spin_lock, 0, ret_any},
+ {"_raw_spin_lock_nested", LOCK, spin_lock, 0, ret_any},
+ {"_raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any},
+ {"__raw_spin_lock", LOCK, spin_lock, 0, ret_any},
+ {"__raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any},
+
+ {"spin_lock_irq", LOCK, spin_lock, 0, ret_any},
+ {"spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
+ {"_spin_lock_irq", LOCK, spin_lock, 0, ret_any},
+ {"_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
+ {"__spin_lock_irq", LOCK, spin_lock, 0, ret_any},
+ {"__spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
+ {"_raw_spin_lock_irq", LOCK, spin_lock, 0, ret_any},
+ {"_raw_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
+ {"__raw_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
+ {"spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
+ {"spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
+ {"_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
+ {"_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
+ {"__spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
+ {"__spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
+ {"_raw_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
+ {"_raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
+ {"__raw_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
+ {"__raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
+ {"spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any},
+ {"_spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any},
+ {"__spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any},
+ {"_raw_spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any},
+ {"spin_lock_bh", LOCK, spin_lock, 0, ret_any},
+ {"spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any},
+ {"_spin_lock_bh", LOCK, spin_lock, 0, ret_any},
+ {"_spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any},
+ {"__spin_lock_bh", LOCK, spin_lock, 0, ret_any},
+ {"__spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any},
+
+ {"spin_trylock", LOCK, spin_lock, 0, ret_one},
+ {"_spin_trylock", LOCK, spin_lock, 0, ret_one},
+ {"__spin_trylock", LOCK, spin_lock, 0, ret_one},
+ {"raw_spin_trylock", LOCK, spin_lock, 0, ret_one},
+ {"_raw_spin_trylock", LOCK, spin_lock, 0, ret_one},
+ {"spin_trylock_irq", LOCK, spin_lock, 0, ret_one},
+ {"spin_trylock_irqsave", LOCK, spin_lock, 0, ret_one},
+ {"spin_trylock_bh", LOCK, spin_lock, 0, ret_one},
+ {"_spin_trylock_bh", LOCK, spin_lock, 0, ret_one},
+ {"__spin_trylock_bh", LOCK, spin_lock, 0, ret_one},
+ {"__raw_spin_trylock", LOCK, spin_lock, 0, ret_one},
+ {"_atomic_dec_and_lock", LOCK, spin_lock, 1, ret_one},
+
+ {"read_lock", LOCK, read_lock, 0, ret_any},
+ {"down_read", LOCK, read_lock, 0, ret_any},
+ {"down_read_nested", LOCK, read_lock, 0, ret_any},
+ {"down_read_trylock", LOCK, read_lock, 0, ret_one},
+ {"up_read", UNLOCK, read_lock, 0, ret_any},
+ {"read_unlock", UNLOCK, read_lock, 0, ret_any},
+ {"_read_lock", LOCK, read_lock, 0, ret_any},
+ {"_read_unlock", UNLOCK, read_lock, 0, ret_any},
+ {"__read_lock", LOCK, read_lock, 0, ret_any},
+ {"__read_unlock", UNLOCK, read_lock, 0, ret_any},
+ {"_raw_read_lock", LOCK, read_lock, 0, ret_any},
+ {"_raw_read_unlock", UNLOCK, read_lock, 0, ret_any},
+ {"__raw_read_lock", LOCK, read_lock, 0, ret_any},
+ {"__raw_read_unlock", UNLOCK, read_lock, 0, ret_any},
+ {"read_lock_irq", LOCK, read_lock, 0, ret_any},
+ {"read_unlock_irq" , UNLOCK, read_lock, 0, ret_any},
+ {"_read_lock_irq", LOCK, read_lock, 0, ret_any},
+ {"_read_unlock_irq", UNLOCK, read_lock, 0, ret_any},
+ {"__read_lock_irq", LOCK, read_lock, 0, ret_any},
+ {"__read_unlock_irq", UNLOCK, read_lock, 0, ret_any},
+ {"_raw_read_unlock_irq", UNLOCK, read_lock, 0, ret_any},
+ {"_raw_read_lock_irq", LOCK, read_lock, 0, ret_any},
+ {"_raw_read_lock_bh", LOCK, read_lock, 0, ret_any},
+ {"_raw_read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
+ {"read_lock_irqsave", LOCK, read_lock, 0, ret_any},
+ {"read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any},
+ {"_read_lock_irqsave", LOCK, read_lock, 0, ret_any},
+ {"_read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any},
+ {"__read_lock_irqsave", LOCK, read_lock, 0, ret_any},
+ {"__read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any},
+ {"read_lock_bh", LOCK, read_lock, 0, ret_any},
+ {"read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
+ {"_read_lock_bh", LOCK, read_lock, 0, ret_any},
+ {"_read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
+ {"__read_lock_bh", LOCK, read_lock, 0, ret_any},
+ {"__read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
+ {"__raw_read_lock_bh", LOCK, read_lock, 0, ret_any},
+ {"__raw_read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
+
+ {"_raw_read_lock_irqsave", LOCK, read_lock, 0, ret_any},
+ {"_raw_read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"_raw_read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any},
+ {"_raw_read_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"_raw_spin_lock_bh", LOCK, read_lock, 0, ret_any},
+ {"_raw_spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"_raw_spin_lock_nest_lock", LOCK, read_lock, 0, ret_any},
+ {"_raw_spin_unlock_bh", UNLOCK, read_lock, 0, ret_any},
+ {"_raw_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"_raw_write_lock_irqsave", LOCK, write_lock, 0, ret_any},
+ {"_raw_write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"_raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
+ {"_raw_write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"__raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
+ {"__raw_write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+
+ {"generic__raw_read_trylock", LOCK, read_lock, 0, ret_one},
+ {"read_trylock", LOCK, read_lock, 0, ret_one},
+ {"_read_trylock", LOCK, read_lock, 0, ret_one},
+ {"raw_read_trylock", LOCK, read_lock, 0, ret_one},
+ {"_raw_read_trylock", LOCK, read_lock, 0, ret_one},
+ {"__raw_read_trylock", LOCK, read_lock, 0, ret_one},
+ {"__read_trylock", LOCK, read_lock, 0, ret_one},
+
+ {"write_lock", LOCK, write_lock, 0, ret_any},
+ {"down_write", LOCK, write_lock, 0, ret_any},
+ {"down_write_nested", LOCK, write_lock, 0, ret_any},
+ {"up_write", UNLOCK, write_lock, 0, ret_any},
+ {"write_unlock", UNLOCK, write_lock, 0, ret_any},
+ {"_write_lock", LOCK, write_lock, 0, ret_any},
+ {"_write_unlock", UNLOCK, write_lock, 0, ret_any},
+ {"__write_lock", LOCK, write_lock, 0, ret_any},
+ {"__write_unlock", UNLOCK, write_lock, 0, ret_any},
+ {"write_lock_irq", LOCK, write_lock, 0, ret_any},
+ {"write_unlock_irq", UNLOCK, write_lock, 0, ret_any},
+ {"_write_lock_irq", LOCK, write_lock, 0, ret_any},
+ {"_write_unlock_irq", UNLOCK, write_lock, 0, ret_any},
+ {"__write_lock_irq", LOCK, write_lock, 0, ret_any},
+ {"__write_unlock_irq", UNLOCK, write_lock, 0, ret_any},
+ {"_raw_write_unlock_irq", UNLOCK, write_lock, 0, ret_any},
+ {"write_lock_irqsave", LOCK, write_lock, 0, ret_any},
+ {"write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
+ {"_write_lock_irqsave", LOCK, write_lock, 0, ret_any},
+ {"_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
+ {"__write_lock_irqsave", LOCK, write_lock, 0, ret_any},
+ {"__write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
+ {"write_lock_bh", LOCK, write_lock, 0, ret_any},
+ {"write_unlock_bh", UNLOCK, write_lock, 0, ret_any},
+ {"_write_lock_bh", LOCK, write_lock, 0, ret_any},
+ {"_write_unlock_bh", UNLOCK, write_lock, 0, ret_any},
+ {"__write_lock_bh", LOCK, write_lock, 0, ret_any},
+ {"__write_unlock_bh", UNLOCK, write_lock, 0, ret_any},
+ {"_raw_write_lock", LOCK, write_lock, 0, ret_any},
+ {"__raw_write_lock", LOCK, write_lock, 0, ret_any},
+ {"_raw_write_unlock", UNLOCK, write_lock, 0, ret_any},
+ {"__raw_write_unlock", UNLOCK, write_lock, 0, ret_any},
+ {"_raw_write_lock_bh", LOCK, write_lock, 0, ret_any},
+ {"_raw_write_unlock_bh", UNLOCK, write_lock, 0, ret_any},
+ {"_raw_write_lock_irq", LOCK, write_lock, 0, ret_any},
+
+ {"write_trylock", LOCK, write_lock, 0, ret_one},
+ {"_write_trylock", LOCK, write_lock, 0, ret_one},
+ {"raw_write_trylock", LOCK, write_lock, 0, ret_one},
+ {"_raw_write_trylock", LOCK, write_lock, 0, ret_one},
+ {"__write_trylock", LOCK, write_lock, 0, ret_one},
+ {"__raw_write_trylock", LOCK, write_lock, 0, ret_one},
+ {"down_write_trylock", LOCK, write_lock, 0, ret_one},
+ {"down_write_killable", LOCK, write_lock, 0, ret_zero},
+
+ {"down", LOCK, sem, 0, ret_any},
+ {"up", UNLOCK, sem, 0, ret_any},
+ {"down_trylock", LOCK, sem, 0, ret_zero},
+ {"down_timeout", LOCK, sem, 0, ret_zero},
+ {"down_interruptible", LOCK, sem, 0, ret_zero},
+ {"down_killable", LOCK, sem, 0, ret_zero},
+
+
+ {"mutex_lock", LOCK, mutex, 0, ret_any},
+ {"mutex_unlock", UNLOCK, mutex, 0, ret_any},
+ {"mutex_lock_nested", LOCK, mutex, 0, ret_any},
+ {"mutex_lock_io", LOCK, mutex, 0, ret_any},
+ {"mutex_lock_io_nested", LOCK, mutex, 0, ret_any},
+
+ {"mutex_lock_interruptible", LOCK, mutex, 0, ret_zero},
+ {"mutex_lock_interruptible_nested", LOCK, mutex, 0, ret_zero},
+ {"mutex_lock_killable", LOCK, mutex, 0, ret_zero},
+ {"mutex_lock_killable_nested", LOCK, mutex, 0, ret_zero},
+
+ {"mutex_trylock", LOCK, mutex, 0, ret_one},
+
+ {"ww_mutex_lock", LOCK, mutex, 0, ret_any},
+ {"__ww_mutex_lock", LOCK, mutex, 0, ret_any},
+ {"ww_mutex_lock_interruptible", LOCK, mutex, 0, ret_zero},
+ {"ww_mutex_unlock", UNLOCK, mutex, 0, ret_any},
+
+ {"raw_local_irq_disable", LOCK, irq, NO_ARG, ret_any},
+ {"raw_local_irq_enable", UNLOCK, irq, NO_ARG, ret_any},
+ {"spin_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"_spin_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"__spin_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"__spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"_raw_spin_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"_raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"__raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"spin_trylock_irq", LOCK, irq, NO_ARG, ret_one},
+ {"read_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"_read_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"_read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"__read_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"_raw_read_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"__read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"_raw_read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"write_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"_write_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"_write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"__write_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"__write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+ {"_raw_write_lock_irq", LOCK, irq, NO_ARG, ret_any},
+ {"_raw_write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+
+ {"arch_local_irq_save", LOCK, irq, RETURN_VAL, ret_any},
+ {"arch_local_irq_restore", RESTORE, irq, 0, ret_any},
+ {"__raw_local_irq_save", LOCK, irq, RETURN_VAL, ret_any},
+ {"raw_local_irq_restore", RESTORE, irq, 0, ret_any},
+ {"spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any},
+ {"spin_lock_irqsave", LOCK, irq, 1, ret_any},
+ {"spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any},
+ {"_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"_spin_lock_irqsave", LOCK, irq, 1, ret_any},
+ {"_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"__spin_lock_irqsave_nested", LOCK, irq, 1, ret_any},
+ {"__spin_lock_irqsave", LOCK, irq, 1, ret_any},
+ {"__spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"_raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"_raw_spin_lock_irqsave", LOCK, irq, 1, ret_any},
+ {"_raw_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"__raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"__raw_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"_raw_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any},
+ {"spin_trylock_irqsave", LOCK, irq, 1, ret_one},
+ {"read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"read_lock_irqsave", LOCK, irq, 1, ret_any},
+ {"read_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"_read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"_read_lock_irqsave", LOCK, irq, 1, ret_any},
+ {"_read_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"__read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"__read_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"write_lock_irqsave", LOCK, irq, 1, ret_any},
+ {"write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"_write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"_write_lock_irqsave", LOCK, irq, 1, ret_any},
+ {"_write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+ {"__write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
+ {"__write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
+
+ {"local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any},
+ {"_local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any},
+ {"__local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any},
+ {"local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"_local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"__local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"_spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"__spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"__spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"_read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"__read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"__read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"_raw_read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"_raw_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"_write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"_write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"__write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"__write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"_raw_write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
+ {"_raw_write_unlock_bh",UNLOCK, bottom_half, NO_ARG, ret_any},
+ {"spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one},
+ {"_spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one},
+ {"__spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one},
+
+ {"ffs_mutex_lock", LOCK, mutex, 0, ret_zero},
+
+ {"clk_prepare_lock", LOCK, prepare_lock, NO_ARG, ret_any},
+ {"clk_prepare_unlock", UNLOCK, prepare_lock, NO_ARG, ret_any},
+ {"clk_enable_lock", LOCK, enable_lock, -1, ret_any},
+ {"clk_enable_unlock", UNLOCK, enable_lock, 0, ret_any},
+
+ {"dma_resv_lock", LOCK, mutex, 0, ret_zero},
+ {"dma_resv_trylock", LOCK, mutex, 0, ret_one},
+ {"dma_resv_lock_interruptible", LOCK, mutex, 0, ret_zero},
+ {"dma_resv_unlock", UNLOCK, mutex, 0, ret_any},
+
+ {"modeset_lock", LOCK, mutex, 0, ret_zero},
+ {"drm_ modeset_lock", LOCK, mutex, 0, ret_zero},
+ {"drm_modeset_lock_single_interruptible", LOCK, mutex, 0, ret_zero},
+ {"modeset_unlock", UNLOCK, mutex, 0, ret_any},
+
+ {"reiserfs_write_lock_nested", LOCK, mutex, 0, ret_any},
+ {"reiserfs_write_unlock_nested", UNLOCK, mutex, 0, ret_any},
+
+ {"rw_lock", LOCK, write_lock, 1, ret_any},
+ {"rw_unlock", UNLOCK, write_lock, 1, ret_any},
+
+ {"sem_lock", LOCK, mutex, 0, ret_any},
+ {"sem_unlock", UNLOCK, mutex, 0, ret_any},
+
+ {},
};
-static struct lock_info kernel_lock_table[] = {
- {"lock_kernel", LOCK, "BKL", NO_ARG, ret_any},
- {"unlock_kernel", UNLOCK, "BKL", NO_ARG, ret_any},
-
- {"spin_lock", LOCK, "spin_lock", 0, ret_any},
- {"spin_unlock", UNLOCK, "spin_lock", 0, ret_any},
- {"spin_lock_nested", LOCK, "spin_lock", 0, ret_any},
- {"_spin_lock", LOCK, "spin_lock", 0, ret_any},
- {"_spin_unlock", UNLOCK, "spin_lock", 0, ret_any},
- {"_spin_lock_nested", LOCK, "spin_lock", 0, ret_any},
- {"__spin_lock", LOCK, "spin_lock", 0, ret_any},
- {"__spin_unlock", UNLOCK, "spin_lock", 0, ret_any},
- {"__spin_lock_nested", LOCK, "spin_lock", 0, ret_any},
- {"raw_spin_lock", LOCK, "spin_lock", 0, ret_any},
- {"raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any},
- {"_raw_spin_lock", LOCK, "spin_lock", 0, ret_any},
- {"_raw_spin_lock_nested", LOCK, "spin_lock", 0, ret_any},
- {"_raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any},
- {"__raw_spin_lock", LOCK, "spin_lock", 0, ret_any},
- {"__raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any},
-
- {"spin_lock_irq", LOCK, "spin_lock", 0, ret_any},
- {"spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any},
- {"_spin_lock_irq", LOCK, "spin_lock", 0, ret_any},
- {"_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any},
- {"__spin_lock_irq", LOCK, "spin_lock", 0, ret_any},
- {"__spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any},
- {"_raw_spin_lock_irq", LOCK, "spin_lock", 0, ret_any},
- {"_raw_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any},
- {"__raw_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any},
- {"spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any},
- {"spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any},
- {"_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any},
- {"_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any},
- {"__spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any},
- {"__spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any},
- {"_raw_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any},
- {"_raw_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any},
- {"__raw_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any},
- {"__raw_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any},
- {"spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any},
- {"_spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any},
- {"__spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any},
- {"_raw_spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any},
- {"spin_lock_bh", LOCK, "spin_lock", 0, ret_any},
- {"spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any},
- {"_spin_lock_bh", LOCK, "spin_lock", 0, ret_any},
- {"_spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any},
- {"__spin_lock_bh", LOCK, "spin_lock", 0, ret_any},
- {"__spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any},
-
- {"spin_trylock", LOCK, "spin_lock", 0, ret_one},
- {"_spin_trylock", LOCK, "spin_lock", 0, ret_one},
- {"__spin_trylock", LOCK, "spin_lock", 0, ret_one},
- {"raw_spin_trylock", LOCK, "spin_lock", 0, ret_one},
- {"_raw_spin_trylock", LOCK, "spin_lock", 0, ret_one},
- {"spin_trylock_irq", LOCK, "spin_lock", 0, ret_one},
- {"spin_trylock_irqsave", LOCK, "spin_lock", 0, ret_one},
- {"spin_trylock_bh", LOCK, "spin_lock", 0, ret_one},
- {"_spin_trylock_bh", LOCK, "spin_lock", 0, ret_one},
- {"__spin_trylock_bh", LOCK, "spin_lock", 0, ret_one},
- {"__raw_spin_trylock", LOCK, "spin_lock", 0, ret_one},
- {"_atomic_dec_and_lock", LOCK, "spin_lock", 1, ret_one},
-
- {"read_lock", LOCK, "read_lock", 0, ret_any},
- {"read_unlock", UNLOCK, "read_lock", 0, ret_any},
- {"_read_lock", LOCK, "read_lock", 0, ret_any},
- {"_read_unlock", UNLOCK, "read_lock", 0, ret_any},
- {"__read_lock", LOCK, "read_lock", 0, ret_any},
- {"__read_unlock", UNLOCK, "read_lock", 0, ret_any},
- {"_raw_read_lock", LOCK, "read_lock", 0, ret_any},
- {"_raw_read_unlock", UNLOCK, "read_lock", 0, ret_any},
- {"__raw_read_lock", LOCK, "read_lock", 0, ret_any},
- {"__raw_read_unlock", UNLOCK, "read_lock", 0, ret_any},
- {"read_lock_irq", LOCK, "read_lock", 0, ret_any},
- {"read_unlock_irq" , UNLOCK, "read_lock", 0, ret_any},
- {"_read_lock_irq", LOCK, "read_lock", 0, ret_any},
- {"_read_unlock_irq", UNLOCK, "read_lock", 0, ret_any},
- {"__read_lock_irq", LOCK, "read_lock", 0, ret_any},
- {"__read_unlock_irq", UNLOCK, "read_lock", 0, ret_any},
- {"read_lock_irqsave", LOCK, "read_lock", 0, ret_any},
- {"read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any},
- {"_read_lock_irqsave", LOCK, "read_lock", 0, ret_any},
- {"_read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any},
- {"__read_lock_irqsave", LOCK, "read_lock", 0, ret_any},
- {"__read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any},
- {"read_lock_bh", LOCK, "read_lock", 0, ret_any},
- {"read_unlock_bh", UNLOCK, "read_lock", 0, ret_any},
- {"_read_lock_bh", LOCK, "read_lock", 0, ret_any},
- {"_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any},
- {"__read_lock_bh", LOCK, "read_lock", 0, ret_any},
- {"__read_unlock_bh", UNLOCK, "read_lock", 0, ret_any},
- {"_raw_read_lock_bh", LOCK, "read_lock", 0, ret_any},
- {"_raw_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any},
- {"__raw_read_lock_bh", LOCK, "read_lock", 0, ret_any},
- {"__raw_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any},
-
- {"generic__raw_read_trylock", LOCK, "read_lock", 0, ret_one},
- {"read_trylock", LOCK, "read_lock", 0, ret_one},
- {"_read_trylock", LOCK, "read_lock", 0, ret_one},
- {"raw_read_trylock", LOCK, "read_lock", 0, ret_one},
- {"_raw_read_trylock", LOCK, "read_lock", 0, ret_one},
- {"__raw_read_trylock", LOCK, "read_lock", 0, ret_one},
- {"__read_trylock", LOCK, "read_lock", 0, ret_one},
-
- {"write_lock", LOCK, "write_lock", 0, ret_any},
- {"write_unlock", UNLOCK, "write_lock", 0, ret_any},
- {"_write_lock", LOCK, "write_lock", 0, ret_any},
- {"_write_unlock", UNLOCK, "write_lock", 0, ret_any},
- {"__write_lock", LOCK, "write_lock", 0, ret_any},
- {"__write_unlock", UNLOCK, "write_lock", 0, ret_any},
- {"write_lock_irq", LOCK, "write_lock", 0, ret_any},
- {"write_unlock_irq", UNLOCK, "write_lock", 0, ret_any},
- {"_write_lock_irq", LOCK, "write_lock", 0, ret_any},
- {"_write_unlock_irq", UNLOCK, "write_lock", 0, ret_any},
- {"__write_lock_irq", LOCK, "write_lock", 0, ret_any},
- {"__write_unlock_irq", UNLOCK, "write_lock", 0, ret_any},
- {"write_lock_irqsave", LOCK, "write_lock", 0, ret_any},
- {"write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any},
- {"_write_lock_irqsave", LOCK, "write_lock", 0, ret_any},
- {"_write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any},
- {"__write_lock_irqsave", LOCK, "write_lock", 0, ret_any},
- {"__write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any},
- {"write_lock_bh", LOCK, "write_lock", 0, ret_any},
- {"write_unlock_bh", UNLOCK, "write_lock", 0, ret_any},
- {"_write_lock_bh", LOCK, "write_lock", 0, ret_any},
- {"_write_unlock_bh", UNLOCK, "write_lock", 0, ret_any},
- {"__write_lock_bh", LOCK, "write_lock", 0, ret_any},
- {"__write_unlock_bh", UNLOCK, "write_lock", 0, ret_any},
- {"_raw_write_lock", LOCK, "write_lock", 0, ret_any},
- {"__raw_write_lock", LOCK, "write_lock", 0, ret_any},
- {"_raw_write_unlock", UNLOCK, "write_lock", 0, ret_any},
- {"__raw_write_unlock", UNLOCK, "write_lock", 0, ret_any},
-
- {"write_trylock", LOCK, "write_lock", 0, ret_one},
- {"_write_trylock", LOCK, "write_lock", 0, ret_one},
- {"raw_write_trylock", LOCK, "write_lock", 0, ret_one},
- {"_raw_write_trylock", LOCK, "write_lock", 0, ret_one},
- {"__write_trylock", LOCK, "write_lock", 0, ret_one},
- {"__raw_write_trylock", LOCK, "write_lock", 0, ret_one},
-
- {"down", LOCK, "sem", 0, ret_any},
- {"up", UNLOCK, "sem", 0, ret_any},
- {"down_trylock", LOCK, "sem", 0, ret_zero},
- {"down_timeout", LOCK, "sem", 0, ret_zero},
- {"down_interruptible", LOCK, "sem", 0, ret_zero},
-
-
- {"down_write", LOCK, "rw_sem", 0, ret_any},
- {"downgrade_write", UNLOCK, "rw_sem", 0, ret_any},
- {"downgrade_write", LOCK, "read_sem", 0, ret_any},
- {"up_write", UNLOCK, "rw_sem", 0, ret_any},
- {"down_write_trylock", LOCK, "rw_sem", 0, ret_one},
- {"down_write_killable", LOCK, "rw_sem", 0, ret_zero},
- {"down_read", LOCK, "read_sem", 0, ret_any},
- {"down_read_trylock", LOCK, "read_sem", 0, ret_one},
- {"down_read_killable", LOCK, "read_sem", 0, ret_zero},
- {"up_read", UNLOCK, "read_sem", 0, ret_any},
-
- {"mutex_lock", LOCK, "mutex", 0, ret_any},
- {"mutex_lock_io", LOCK, "mutex", 0, ret_any},
- {"mutex_unlock", UNLOCK, "mutex", 0, ret_any},
- {"mutex_lock_nested", LOCK, "mutex", 0, ret_any},
- {"mutex_lock_io_nested", LOCK, "mutex", 0, ret_any},
-
- {"mutex_lock_interruptible", LOCK, "mutex", 0, ret_zero},
- {"mutex_lock_interruptible_nested", LOCK, "mutex", 0, ret_zero},
- {"mutex_lock_killable", LOCK, "mutex", 0, ret_zero},
- {"mutex_lock_killable_nested", LOCK, "mutex", 0, ret_zero},
-
- {"mutex_trylock", LOCK, "mutex", 0, ret_one},
-
- {"raw_local_irq_disable", LOCK, "irq", NO_ARG, ret_any},
- {"raw_local_irq_enable", UNLOCK, "irq", NO_ARG, ret_any},
- {"spin_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"_spin_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"__spin_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"__spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"_raw_spin_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"_raw_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"__raw_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"spin_trylock_irq", LOCK, "irq", NO_ARG, ret_one},
- {"read_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"_read_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"_read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"__read_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"__read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"write_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"_write_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"_write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
- {"__write_lock_irq", LOCK, "irq", NO_ARG, ret_any},
- {"__write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
-
- {"arch_local_irq_save", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"arch_local_irq_restore", UNLOCK, "irqsave", 0, ret_any},
- {"__raw_local_irq_save", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"raw_local_irq_restore", UNLOCK, "irqsave", 0, ret_any},
- {"spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"spin_lock_irqsave", LOCK, "irqsave", 1, ret_any},
- {"spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
- {"_spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"_spin_lock_irqsave", LOCK, "irqsave", 1, ret_any},
- {"_spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
- {"__spin_lock_irqsave_nested", LOCK, "irqsave", 1, ret_any},
- {"__spin_lock_irqsave", LOCK, "irqsave", 1, ret_any},
- {"__spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
- {"_raw_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"_raw_spin_lock_irqsave", LOCK, "irqsave", 1, ret_any},
- {"_raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any},
- {"__raw_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"__raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any},
- {"_raw_spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"spin_trylock_irqsave", LOCK, "irqsave", 1, ret_one},
- {"read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"read_lock_irqsave", LOCK, "irqsave", 1, ret_any},
- {"read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
- {"_read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"_read_lock_irqsave", LOCK, "irqsave", 1, ret_any},
- {"_read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
- {"__read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"__read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
- {"write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"write_lock_irqsave", LOCK, "irqsave", 1, ret_any},
- {"write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
- {"_write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"_write_lock_irqsave", LOCK, "irqsave", 1, ret_any},
- {"_write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
- {"__write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any},
- {"__write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any},
-
- {"local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any},
- {"_local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any},
- {"__local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any},
- {"local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"_local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"__local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"_spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"_spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"__spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"__spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"_read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"_read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"__read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"__read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"_raw_read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"_raw_read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"_write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"_write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"__write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any},
- {"__write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
- {"spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one},
- {"_spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one},
- {"__spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one},
-
- {"ffs_mutex_lock", LOCK, "mutex", 0, ret_zero},
+struct macro_info {
+ const char *macro;
+ enum action action;
+ int param;
};
-static struct lock_info *lock_table;
-
-static struct tracker_list *starts_locked;
-static struct tracker_list *starts_unlocked;
+static struct macro_info macro_table[] = {
+ {"genpd_lock", LOCK, 0},
+ {"genpd_lock_nested", LOCK, 0},
+ {"genpd_lock_interruptible", LOCK, 0},
+ {"genpd_unlock", UNLOCK, 0},
+};
-struct locks_on_return {
- int line;
- struct tracker_list *locked;
- struct tracker_list *unlocked;
- struct tracker_list *impossible;
- struct range_list *return_values;
+static const char *false_positives[][2] = {
+ {"fs/jffs2/", "->alloc_sem"},
+ {"fs/xfs/", "->b_sema"},
+ {"mm/", "pvmw->ptl"},
};
-DECLARE_PTR_LIST(return_list, struct locks_on_return);
-static struct return_list *all_returns;
-static char *make_full_name(const char *lock, const char *var)
-{
- static char tmp_buf[512];
+static struct stree *start_states;
+static struct stree_stack *saved_stack;
- snprintf(tmp_buf, sizeof(tmp_buf), "%s:%s", lock, var);
- remove_parens(tmp_buf);
- return alloc_string(tmp_buf);
+static struct tracker_list *locks;
+
+static void reset(struct sm_state *sm, struct expression *mod_expr)
+{
+ set_state(my_id, sm->name, sm->sym, &start_state);
}
static struct expression *remove_spinlock_check(struct expression *expr)
@@ -406,139 +450,320 @@ static struct expression *remove_spinlock_check(struct expression *expr)
return expr;
}
-static char *get_full_name(struct expression *expr, int index)
+static struct expression *filter_kernel_args(struct expression *arg)
+{
+ if (arg->type == EXPR_PREOP && arg->op == '&')
+ return strip_expr(arg->unop);
+ if (!is_pointer(arg))
+ return arg;
+ return deref_expression(strip_expr(arg));
+}
+
+static char *lock_to_name_sym(struct expression *expr, struct symbol **sym)
+{
+ expr = remove_spinlock_check(expr);
+ expr = filter_kernel_args(expr);
+ return expr_to_str_sym(expr, sym);
+}
+
+static char *get_full_name(struct expression *expr, int index, struct symbol **sym)
{
- struct expression *arg;
- char *name = NULL;
- char *full_name = NULL;
struct lock_info *lock = &lock_table[index];
+ struct expression *arg;
+ *sym = NULL;
if (lock->arg == RETURN_VAL) {
- name = expr_to_var(expr->left);
- full_name = make_full_name(lock->name, name);
+ return expr_to_var_sym(strip_expr(expr->left), sym);
} else if (lock->arg == NO_ARG) {
- full_name = make_full_name(lock->name, "");
+ return alloc_string(get_lock_name(lock->type));
} else {
arg = get_argument_from_call_expr(expr->args, lock->arg);
if (!arg)
- goto free;
- arg = remove_spinlock_check(arg);
- name = expr_to_str(arg);
- if (!name)
- goto free;
- full_name = make_full_name(lock->name, name);
+ return NULL;
+ return lock_to_name_sym(arg, sym);
}
-free:
- free_string(name);
- return full_name;
}
-static struct smatch_state *get_start_state(struct sm_state *sm)
+static struct smatch_state *unmatched_state(struct sm_state *sm)
+{
+ return &start_state;
+}
+
+static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
{
- int is_locked = 0;
- int is_unlocked = 0;
+ if (is_impossible_path())
+ set_state(my_id, cur->name, cur->sym, &impossible);
+}
- if (in_tracker_list(starts_locked, my_id, sm->name, sm->sym))
- is_locked = 1;
- if (in_tracker_list(starts_unlocked, my_id, sm->name, sm->sym))
- is_unlocked = 1;
- if (is_locked && is_unlocked)
- return &undefined;
- if (is_locked)
+static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
+{
+ if (s1 == &impossible)
+ return s2;
+ if (s2 == &impossible)
+ return s1;
+ return &merged;
+}
+
+static struct smatch_state *action_to_state(enum action lock_unlock)
+{
+ switch (lock_unlock) {
+ case LOCK:
return &locked;
- if (is_unlocked)
+ case UNLOCK:
return &unlocked;
- return &undefined;
+ case RESTORE:
+ return &restore;
+ }
+ return NULL;
}
-static struct smatch_state *unmatched_state(struct sm_state *sm)
+static struct sm_state *get_best_match(const char *key, enum action lock_unlock)
{
- return &start_state;
+ struct sm_state *sm;
+ struct sm_state *match;
+ int cnt = 0;
+ int start_pos, state_len, key_len, chunks, i;
+
+ if (strncmp(key, "$->", 3) == 0)
+ key += 3;
+
+ key_len = strlen(key);
+ chunks = 0;
+ for (i = key_len - 1; i > 0; i--) {
+ if (key[i] == '>' || key[i] == '.')
+ chunks++;
+ if (chunks == 2) {
+ key += (i + 1);
+ key_len = strlen(key);
+ break;
+ }
+ }
+
+ FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+ if (((lock_unlock == UNLOCK || lock_unlock == RESTORE) &&
+ sm->state != &locked) ||
+ (lock_unlock == LOCK && sm->state != &unlocked))
+ continue;
+ state_len = strlen(sm->name);
+ if (state_len < key_len)
+ continue;
+ start_pos = state_len - key_len;
+ if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
+ strcmp(sm->name + start_pos, key) == 0) {
+ cnt++;
+ match = sm;
+ }
+ } END_FOR_EACH_SM(sm);
+
+ if (cnt == 1)
+ return match;
+ return NULL;
}
-static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
+static void use_best_match(char *key, enum action lock_unlock)
{
- if (is_impossible_path())
- set_state(my_id, cur->name, cur->sym, &impossible);
+ struct sm_state *match;
+
+ match = get_best_match(key, lock_unlock);
+ if (match)
+ set_state(my_id, match->name, match->sym, action_to_state(lock_unlock));
+ else
+ set_state(my_id, key, NULL, action_to_state(lock_unlock));
}
-static bool nestable(const char *name)
+static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start)
{
- if (strstr(name, "read_sem:"))
- return true;
- if (strcmp(name, "bottom_half:") == 0)
- return true;
+ struct smatch_state *orig;
+
+ orig = get_state_stree(start_states, my_id, name, sym);
+ if (!orig)
+ set_state_stree(&start_states, my_id, name, sym, start);
+ else if (orig != start)
+ set_state_stree(&start_states, my_id, name, sym, &undefined);
+}
+
+static bool common_false_positive(const char *name)
+{
+ const char *path, *lname;
+ int i, len_total, len_path, len_name, skip;
+
+ if (!get_filename())
+ return false;
+
+ len_total = strlen(name);
+ for (i = 0; i < ARRAY_SIZE(false_positives); i++) {
+ path = false_positives[i][0];
+ lname = false_positives[i][1];
+
+ len_path = strlen(path);
+ len_name = strlen(lname);
+
+ if (len_name > len_total)
+ continue;
+ skip = len_total - len_name;
+
+ if (strncmp(get_filename(), path, len_path) == 0 &&
+ strcmp(name + skip, lname) == 0)
+ return true;
+ }
+
return false;
}
-static void do_lock(const char *name)
+static void warn_on_double(struct sm_state *sm, struct smatch_state *state)
{
- struct sm_state *sm;
+ struct sm_state *tmp;
- if (__inline_fn)
+ if (!sm)
return;
- sm = get_sm_state(my_id, name, NULL);
- if (!sm)
- add_tracker(&starts_unlocked, my_id, name, NULL);
- if (sm && slist_has_state(sm->possible, &locked) && !nestable(name))
- sm_error("double lock '%s'", name);
- if (sm)
- func_has_transition = TRUE;
- set_state(my_id, name, NULL, &locked);
+ FOR_EACH_PTR(sm->possible, tmp) {
+ if (tmp->state == state)
+ goto found;
+ } END_FOR_EACH_PTR(tmp);
+
+ return;
+found:
+ if (strcmp(sm->name, "bottom_half") == 0)
+ return;
+ if (common_false_positive(sm->name))
+ return;
+ sm_msg("error: double %s '%s' (orig line %u)",
+ state->name, sm->name, tmp->line);
}
-static void do_lock_failed(const char *name)
+static bool handle_macro_lock_unlock(void)
{
+ struct expression *expr, *arg;
+ struct macro_info *info;
struct sm_state *sm;
+ struct symbol *sym;
+ const char *macro;
+ char *name;
+ bool ret = false;
+ int i;
- if (__inline_fn)
+ expr = last_ptr_list((struct ptr_list *)big_expression_stack);
+ while (expr && expr->type == EXPR_ASSIGNMENT)
+ expr = strip_expr(expr->right);
+ if (!expr || expr->type != EXPR_CALL)
+ return false;
+
+ macro = get_macro_name(expr->pos);
+ if (!macro)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(macro_table); i++) {
+ info = &macro_table[i];
+
+ if (strcmp(macro, info->macro) != 0)
+ continue;
+ arg = get_argument_from_call_expr(expr->args, info->param);
+ name = expr_to_str_sym(arg, &sym);
+ if (!name || !sym)
+ goto free;
+ sm = get_sm_state(my_id, name, sym);
+
+ if (info->action == LOCK) {
+ if (!sm)
+ set_start_state(name, sym, &unlocked);
+ if (sm && sm->line != expr->pos.line)
+ warn_on_double(sm, &locked);
+ set_state(my_id, name, sym, &locked);
+ } else {
+ if (!sm)
+ set_start_state(name, sym, &locked);
+ if (sm && sm->line != expr->pos.line)
+ warn_on_double(sm, &unlocked);
+ set_state(my_id, name, sym, &unlocked);
+ }
+ ret = true;
+free:
+ free_string(name);
+ return ret;
+ }
+ return false;
+}
+
+static void do_lock(const char *name, struct symbol *sym, struct lock_info *info)
+{
+ struct sm_state *sm;
+
+ if (handle_macro_lock_unlock())
return;
- sm = get_sm_state(my_id, name, NULL);
+ add_tracker(&locks, my_id, name, sym);
+
+ sm = get_sm_state(my_id, name, sym);
if (!sm)
- add_tracker(&starts_unlocked, my_id, name, NULL);
- set_state(my_id, name, NULL, &unlocked);
+ set_start_state(name, sym, &unlocked);
+ warn_on_double(sm, &locked);
+ set_state(my_id, name, sym, &locked);
+}
+
+static void do_lock_failed(const char *name, struct symbol *sym)
+{
+ add_tracker(&locks, my_id, name, sym);
+ set_state(my_id, name, sym, &unlocked);
}
-static void do_unlock(const char *name)
+static void do_unlock(const char *name, struct symbol *sym, struct lock_info *info)
{
struct sm_state *sm;
- if (__inline_fn)
- return;
if (__path_is_null())
return;
- sm = get_sm_state(my_id, name, NULL);
+
+ if (handle_macro_lock_unlock())
+ return;
+
+ add_tracker(&locks, my_id, name, sym);
+ sm = get_sm_state(my_id, name, sym);
+ if (!sm) {
+ sm = get_best_match(name, UNLOCK);
+ if (sm) {
+ name = sm->name;
+ sym = sm->sym;
+ }
+ }
if (!sm)
- add_tracker(&starts_locked, my_id, name, NULL);
- if (sm && slist_has_state(sm->possible, &unlocked) &&
- strcmp(name, "bottom_half:") != 0)
- sm_error("double unlock '%s'", name);
- if (sm)
- func_has_transition = TRUE;
- set_state(my_id, name, NULL, &unlocked);
+ set_start_state(name, sym, &locked);
+ warn_on_double(sm, &unlocked);
+ set_state(my_id, name, sym, &unlocked);
+}
+
+static void do_restore(const char *name, struct symbol *sym, struct lock_info *info)
+{
+ if (__path_is_null())
+ return;
+
+ if (!get_state(my_id, name, sym))
+ set_start_state(name, sym, &locked);
+
+ add_tracker(&locks, my_id, name, sym);
+ set_state(my_id, name, sym, &restore);
}
static void match_lock_held(const char *fn, struct expression *call_expr,
- struct expression *assign_expr, void *_index)
+ struct expression *assign_expr, void *_index)
{
int index = PTR_INT(_index);
- char *lock_name;
struct lock_info *lock = &lock_table[index];
+ char *lock_name;
+ struct symbol *sym;
if (lock->arg == NO_ARG) {
- lock_name = get_full_name(NULL, index);
+ lock_name = get_full_name(NULL, index, &sym);
} else if (lock->arg == RETURN_VAL) {
if (!assign_expr)
return;
- lock_name = get_full_name(assign_expr, index);
+ lock_name = get_full_name(assign_expr, index, &sym);
} else {
- lock_name = get_full_name(call_expr, index);
+ lock_name = get_full_name(call_expr, index, &sym);
}
if (!lock_name)
return;
- do_lock(lock_name);
+ do_lock(lock_name, sym, lock);
free_string(lock_name);
}
@@ -546,419 +771,474 @@ static void match_lock_failed(const char *fn, struct expression *call_expr,
struct expression *assign_expr, void *_index)
{
int index = PTR_INT(_index);
- char *lock_name;
struct lock_info *lock = &lock_table[index];
+ char *lock_name;
+ struct symbol *sym;
if (lock->arg == NO_ARG) {
- lock_name = get_full_name(NULL, index);
+ lock_name = get_full_name(NULL, index, &sym);
} else if (lock->arg == RETURN_VAL) {
if (!assign_expr)
return;
- lock_name = get_full_name(assign_expr, index);
+ lock_name = get_full_name(assign_expr, index, &sym);
} else {
- lock_name = get_full_name(call_expr, index);
+ lock_name = get_full_name(call_expr, index, &sym);
}
if (!lock_name)
return;
- do_lock_failed(lock_name);
+ do_lock_failed(lock_name, sym);
free_string(lock_name);
}
static void match_returns_locked(const char *fn, struct expression *expr,
void *_index)
{
- char *full_name = NULL;
int index = PTR_INT(_index);
struct lock_info *lock = &lock_table[index];
+ char *full_name;
+ struct symbol *sym;
if (lock->arg != RETURN_VAL)
return;
- full_name = get_full_name(expr, index);
- do_lock(full_name);
+ full_name = get_full_name(expr, index, &sym);
+ if (!full_name)
+ return;
+ do_lock(full_name, sym, lock);
}
static void match_lock_unlock(const char *fn, struct expression *expr, void *_index)
{
- char *full_name = NULL;
int index = PTR_INT(_index);
struct lock_info *lock = &lock_table[index];
+ char *full_name;
+ struct symbol *sym;
- if (__inline_fn)
- return;
-
- full_name = get_full_name(expr, index);
+ full_name = get_full_name(expr, index, &sym);
if (!full_name)
return;
- if (lock->action == LOCK)
- do_lock(full_name);
- else
- do_unlock(full_name);
+ switch (lock->action) {
+ case LOCK:
+ do_lock(full_name, sym, lock);
+ break;
+ case UNLOCK:
+ do_unlock(full_name, sym, lock);
+ break;
+ case RESTORE:
+ do_restore(full_name, sym, lock);
+ break;
+ }
free_string(full_name);
}
-static struct locks_on_return *alloc_return(struct expression *expr)
+static struct smatch_state *get_start_state(struct sm_state *sm)
{
- struct locks_on_return *ret;
+ struct smatch_state *orig;
- ret = malloc(sizeof(*ret));
- if (!get_implied_rl(expr, &ret->return_values))
- ret->return_values = NULL;
- ret->line = get_lineno();
- ret->locked = NULL;
- ret->unlocked = NULL;
- ret->impossible = NULL;
- return ret;
+ orig = get_state_stree(start_states, my_id, sm->name, sm->sym);
+ if (orig)
+ return orig;
+ return &undefined;
}
-static int check_possible(struct sm_state *sm)
+static int get_param_lock_name(struct sm_state *sm, struct expression *expr,
+ const char **name)
{
- struct sm_state *tmp;
- int islocked = 0;
- int isunlocked = 0;
- int undef = 0;
-
- if (!option_spammy)
- return 0;
+ char *other_name;
+ struct symbol *other_sym;
+ const char *param_name;
+ int param;
+
+ *name = sm->name;
+
+ param = get_param_num_from_sym(sm->sym);
+ if (param >= 0) {
+ param_name = get_param_name(sm);
+ if (param_name)
+ *name = param_name;
+ return param;
+ }
- FOR_EACH_PTR(sm->possible, tmp) {
- if (tmp->state == &locked)
- islocked = 1;
- if (tmp->state == &unlocked)
- isunlocked = 1;
- if (tmp->state == &start_state) {
- struct smatch_state *s;
-
- s = get_start_state(tmp);
- if (s == &locked)
- islocked = 1;
- else if (s == &unlocked)
- isunlocked = 1;
- else
- undef = 1;
+ if (expr) {
+ struct symbol *ret_sym;
+ char *ret_str;
+
+ ret_str = expr_to_str_sym(expr, &ret_sym);
+ if (ret_str && ret_sym == sm->sym) {
+ param_name = state_name_to_param_name(sm->name, ret_str);
+ if (param_name) {
+ free_string(ret_str);
+ *name = param_name;
+ return -1;
+ }
}
- if (tmp->state == &undefined)
- undef = 1; // i don't think this is possible any more.
- } END_FOR_EACH_PTR(tmp);
- if ((islocked && isunlocked) || undef) {
- sm_warning("'%s' is sometimes locked here and sometimes unlocked.", sm->name);
- return 1;
+ free_string(ret_str);
}
- return 0;
-}
-static struct position warned_pos;
+ other_name = get_other_name_sym(sm->name, sm->sym, &other_sym);
+ if (!other_name)
+ return -2;
+ param = get_param_num_from_sym(other_sym);
+ if (param < 0)
+ return -2;
+
+ param_name = get_param_name_var_sym(other_name, other_sym);
+ free_string(other_name);
+ if (param_name)
+ *name = param_name;
+ return param;
+}
-static void match_return(int return_id, char *return_ranges, struct expression *expr)
+static int get_db_type(struct sm_state *sm)
{
- struct locks_on_return *ret;
- struct stree *stree;
- struct sm_state *tmp;
+ if (sm->state == get_start_state(sm)) {
+ if (sm->state == &locked)
+ return KNOWN_LOCKED;
+ if (sm->state == &unlocked)
+ return KNOWN_UNLOCKED;
+ }
- if (!final_pass)
- return;
- if (__inline_fn)
- return;
+ if (sm->state == &locked)
+ return LOCKED;
+ if (sm->state == &unlocked)
+ return UNLOCKED;
+ if (sm->state == &restore)
+ return LOCK_RESTORED;
+ return LOCKED;
+}
- if (expr && cmp_pos(expr->pos, warned_pos) == 0)
- return;
+static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
+{
+ struct sm_state *sm;
+ const char *param_name;
+ int param;
- ret = alloc_return(expr);
+ FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+ if (sm->state != &locked &&
+ sm->state != &unlocked &&
+ sm->state != &restore)
+ continue;
- stree = __get_cur_stree();
- FOR_EACH_MY_SM(my_id, stree, tmp) {
- if (tmp->state == &locked) {
- add_tracker(&ret->locked, tmp->owner, tmp->name,
- tmp->sym);
- } else if (tmp->state == &unlocked) {
- add_tracker(&ret->unlocked, tmp->owner, tmp->name,
- tmp->sym);
- } else if (tmp->state == &start_state) {
- struct smatch_state *s;
-
- s = get_start_state(tmp);
- if (s == &locked)
- add_tracker(&ret->locked, tmp->owner, tmp->name,
- tmp->sym);
- if (s == &unlocked)
- add_tracker(&ret->unlocked, tmp->owner,tmp->name,
- tmp->sym);
- } else if (tmp->state == &impossible) {
- add_tracker(&ret->impossible, tmp->owner, tmp->name,
- tmp->sym);
- } else {
- if (check_possible(tmp)) {
- if (expr)
- warned_pos = expr->pos;
- }
- }
- } END_FOR_EACH_SM(tmp);
- add_ptr_list(&all_returns, ret);
+ param = get_param_lock_name(sm, expr, &param_name);
+ sql_insert_return_states(return_id, return_ranges,
+ get_db_type(sm),
+ param, param_name, "");
+ } END_FOR_EACH_SM(sm);
}
-static void add_line(struct range_list **rl, int line)
+enum {
+ ERR_PTR, VALID_PTR, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS,
+};
+
+static bool is_EINTR(struct range_list *rl)
{
- sval_t sval = sval_type_val(&int_ctype, line);
+ sval_t sval;
- add_range(rl, sval, sval);
+ if (!rl_to_sval(rl, &sval))
+ return false;
+ return sval.value == -4;
}
-static int line_printed(struct range_list *rl, int line)
+static int success_fail_positive(struct range_list *rl)
{
- sval_t sval = sval_type_val(&int_ctype, line);
+ /* void returns are the same as success (zero in the kernel) */
+ if (!rl)
+ return ZERO;
+
+ if (rl_type(rl)->type != SYM_PTR && sval_is_negative(rl_min(rl)))
+ return NEGATIVE;
+
+ if (rl_min(rl).value == 0 && rl_max(rl).value == 0)
+ return ZERO;
+
+ if (is_err_ptr(rl_min(rl)) &&
+ is_err_ptr(rl_max(rl)))
+ return ERR_PTR;
+
+ /*
+ * Trying to match ERR_PTR(ret) but without the expression struct.
+ * Ugly...
+ */
+ if (type_bits(&long_ctype) == 64 &&
+ rl_type(rl)->type == SYM_PTR &&
+ rl_min(rl).value == INT_MIN)
+ return ERR_PTR;
+
+ return POSITIVE;
+}
+
+static bool sym_in_lock_table(struct symbol *sym)
+{
+ int i;
- return rl_has_sval(rl, sval);
+ if (!sym || !sym->ident)
+ return false;
+
+ for (i = 0; lock_table[i].function != NULL; i++) {
+ if (strcmp(lock_table[i].function, sym->ident->name) == 0)
+ return true;
+ }
+ return false;
}
-static void print_inconsistent_returns(struct tracker *lock,
- struct smatch_state *start)
+static bool func_in_lock_table(struct expression *expr)
{
- struct locks_on_return *tmp;
- struct range_list *printed = NULL;
+ if (expr->type != EXPR_SYMBOL)
+ return false;
+ return sym_in_lock_table(expr->symbol);
+}
+
+static void check_lock(char *name, struct symbol *sym)
+{
+ struct range_list *locked_lines = NULL;
+ struct range_list *unlocked_lines = NULL;
+ int locked_buckets[NUM_BUCKETS] = {};
+ int unlocked_buckets[NUM_BUCKETS] = {};
+ struct stree *stree, *orig;
+ struct sm_state *return_sm;
+ struct sm_state *sm;
+ sval_t line = sval_type_val(&int_ctype, 0);
+ int bucket;
int i;
- sm_warning("inconsistent returns '%s'.", lock->name);
- sm_printf(" Locked on: ");
+ if (sym_in_lock_table(cur_func_sym))
+ return;
- i = 0;
- FOR_EACH_PTR(all_returns, tmp) {
- if (line_printed(printed, tmp->line))
- continue;
- if (in_tracker_list(tmp->unlocked, lock->owner, lock->name, lock->sym))
- continue;
- if (in_tracker_list(tmp->locked, lock->owner, lock->name, lock->sym)) {
- if (i++)
- sm_printf(" ");
- sm_printf("line %d\n", tmp->line);
- add_line(&printed, tmp->line);
- continue;
- }
- if (start == &locked) {
- if (i++)
- sm_printf(" ");
- sm_printf("line %d\n", tmp->line);
- add_line(&printed, tmp->line);
- }
- } END_FOR_EACH_PTR(tmp);
+ FOR_EACH_PTR(get_all_return_strees(), stree) {
+ orig = __swap_cur_stree(stree);
- sm_printf(" Unlocked on: ");
- printed = NULL;
- i = 0;
- FOR_EACH_PTR(all_returns, tmp) {
- if (line_printed(printed, tmp->line))
- continue;
- if (in_tracker_list(tmp->unlocked, lock->owner, lock->name, lock->sym)) {
- if (i++)
- sm_printf(" ");
- sm_printf("line %d\n", tmp->line);
- add_line(&printed, tmp->line);
- continue;
+ if (is_impossible_path())
+ goto swap_stree;
+
+ return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
+ if (!return_sm)
+ goto swap_stree;
+ line.value = return_sm->line;
+
+ sm = get_sm_state(my_id, name, sym);
+ if (!sm)
+ goto swap_stree;
+
+ if (parent_is_gone_var_sym(sm->name, sm->sym))
+ goto swap_stree;
+
+ if (sm->state != &locked && sm->state != &unlocked)
+ goto swap_stree;
+
+ if (sm->state == &unlocked && is_EINTR(estate_rl(return_sm->state)))
+ goto swap_stree;
+
+ bucket = success_fail_positive(estate_rl(return_sm->state));
+ if (sm->state == &locked) {
+ add_range(&locked_lines, line, line);
+ locked_buckets[bucket] = true;
}
- if (in_tracker_list(tmp->locked, lock->owner, lock->name, lock->sym))
- continue;
- if (start == &unlocked) {
- if (i++)
- sm_printf(" ");
- sm_printf("line %d\n", tmp->line);
- add_line(&printed, tmp->line);
+ if (sm->state == &unlocked) {
+ add_range(&unlocked_lines, line, line);
+ unlocked_buckets[bucket] = true;
}
- } END_FOR_EACH_PTR(tmp);
-}
+swap_stree:
+ __swap_cur_stree(orig);
+ } END_FOR_EACH_PTR(stree);
-static int matches_return_type(struct range_list *rl, enum return_type type)
-{
- sval_t zero_sval = ll_to_sval(0);
- sval_t one_sval = ll_to_sval(1);
- /* All these double negatives are super ugly! */
+ if (!locked_lines || !unlocked_lines)
+ return;
- switch (type) {
- case ret_zero:
- return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(zero_sval, zero_sval));
- case ret_one:
- return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(one_sval, one_sval));
- case ret_non_zero:
- return !possibly_true_rl(rl, SPECIAL_EQUAL, alloc_rl(zero_sval, zero_sval));
- case ret_negative:
- return !possibly_true_rl(rl, SPECIAL_GTE, alloc_rl(zero_sval, zero_sval));
- case ret_positive:
- return !possibly_true_rl(rl, '<', alloc_rl(zero_sval, zero_sval));
- case ret_any:
- default:
- return 1;
+ for (i = 0; i < NUM_BUCKETS; i++) {
+ if (locked_buckets[i] && unlocked_buckets[i])
+ goto complain;
}
-}
+ if (locked_buckets[NEGATIVE] &&
+ (unlocked_buckets[ZERO] || unlocked_buckets[POSITIVE]))
+ goto complain;
-static int match_held(struct tracker *lock, struct locks_on_return *this_return, struct smatch_state *start)
-{
- if (in_tracker_list(this_return->impossible, lock->owner, lock->name, lock->sym))
- return 0;
- if (in_tracker_list(this_return->unlocked, lock->owner, lock->name, lock->sym))
- return 0;
- if (in_tracker_list(this_return->locked, lock->owner, lock->name, lock->sym))
- return 1;
- if (start == &unlocked)
- return 0;
- return 1;
-}
+ if (locked_buckets[ERR_PTR])
+ goto complain;
-static int match_released(struct tracker *lock, struct locks_on_return *this_return, struct smatch_state *start)
-{
- if (in_tracker_list(this_return->impossible, lock->owner, lock->name, lock->sym))
- return 0;
- if (in_tracker_list(this_return->unlocked, lock->owner, lock->name, lock->sym))
- return 1;
- if (in_tracker_list(this_return->locked, lock->owner, lock->name, lock->sym))
- return 0;
- if (start == &unlocked)
- return 1;
- return 0;
+ return;
+
+complain:
+ sm_msg("warn: inconsistent returns '%s'.", name);
+ sm_printf(" Locked on : %s\n", show_rl(locked_lines));
+ sm_printf(" Unlocked on: %s\n", show_rl(unlocked_lines));
}
-static int held_on_return(struct tracker *lock, struct smatch_state *start, enum return_type type)
+static void match_func_end(struct symbol *sym)
{
- struct locks_on_return *tmp;
+ struct tracker *tracker;
- FOR_EACH_PTR(all_returns, tmp) {
- if (!matches_return_type(tmp->return_values, type))
- continue;
- if (match_held(lock, tmp, start))
- return 1;
- } END_FOR_EACH_PTR(tmp);
- return 0;
+ FOR_EACH_PTR(locks, tracker) {
+ check_lock(tracker->name, tracker->sym);
+ } END_FOR_EACH_PTR(tracker);
}
-static int released_on_return(struct tracker *lock, struct smatch_state *start, enum return_type type)
+static void register_lock(int index)
{
- struct locks_on_return *tmp;
+ struct lock_info *lock = &lock_table[index];
+ void *idx = INT_PTR(index);
- FOR_EACH_PTR(all_returns, tmp) {
- if (!matches_return_type(tmp->return_values, type))
- continue;
- if (match_released(lock, tmp, start))
- return 1;
- } END_FOR_EACH_PTR(tmp);
- return 0;
+ if (lock->return_type == ret_one) {
+ return_implies_state(lock->function, 1, 1, &match_lock_held, idx);
+ return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
+ } else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) {
+ add_function_assign_hook(lock->function, &match_returns_locked, idx);
+ } else if (lock->return_type == ret_any) {
+ add_function_hook(lock->function, &match_lock_unlock, idx);
+ } else if (lock->return_type == ret_zero) {
+ return_implies_state(lock->function, 0, 0, &match_lock_held, idx);
+ return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx);
+ } else if (lock->return_type == ret_valid_ptr) {
+ return_implies_state_sval(lock->function, valid_ptr_min_sval, valid_ptr_max_sval, &match_lock_held, idx);
+ }
}
-static void check_returns_consistently(struct tracker *lock,
- struct smatch_state *start)
+static void load_table(struct lock_info *lock_table)
{
- struct symbol *type;
+ int i;
- if (!held_on_return(lock, start, ret_any) ||
- !released_on_return(lock, start, ret_any))
- return;
+ for (i = 0; lock_table[i].function != NULL; i++) {
+ if (lock_table[i].action == LOCK)
+ register_lock(i);
+ else
+ add_function_hook(lock_table[i].function, &match_lock_unlock, INT_PTR(i));
+ }
+}
- if (held_on_return(lock, start, ret_zero) &&
- !held_on_return(lock, start, ret_non_zero))
+static void db_param_locked_unlocked(struct expression *expr, int param, char *key, char *value, enum action lock_unlock)
+{
+ struct expression *call, *arg;
+ char *name;
+ struct symbol *sym;
+
+ call = expr;
+ while (call->type == EXPR_ASSIGNMENT)
+ call = strip_expr(call->right);
+ if (call->type != EXPR_CALL)
return;
- if (held_on_return(lock, start, ret_positive) &&
- !held_on_return(lock, start, ret_zero))
+ if (func_in_lock_table(call->fn))
return;
- if (held_on_return(lock, start, ret_positive) &&
- !held_on_return(lock, start, ret_negative))
+ if (param == -2) {
+ use_best_match(key, lock_unlock);
return;
+ }
- type = cur_func_return_type();
- if (type && type->type == SYM_PTR) {
- if (held_on_return(lock, start, ret_non_zero) &&
- !held_on_return(lock, start, ret_zero))
+ if (param == -1) {
+ if (expr->type != EXPR_ASSIGNMENT)
+ return;
+ name = get_variable_from_key(expr->left, key, &sym);
+ } else {
+ arg = get_argument_from_call_expr(call->args, param);
+ if (!arg)
return;
+
+ name = get_variable_from_key(arg, key, &sym);
}
+ if (!name || !sym)
+ goto free;
- print_inconsistent_returns(lock, start);
+ if (lock_unlock == LOCK)
+ do_lock(name, sym, NULL);
+ else if (lock_unlock == UNLOCK)
+ do_unlock(name, sym, NULL);
+ else if (lock_unlock == RESTORE)
+ do_restore(name, sym, NULL);
+
+free:
+ free_string(name);
}
-static void check_consistency(struct symbol *sym)
+static void db_param_locked(struct expression *expr, int param, char *key, char *value)
{
- struct tracker *tmp;
-
- FOR_EACH_PTR(starts_locked, tmp) {
- if (in_tracker_list(starts_unlocked, tmp->owner, tmp->name,
- tmp->sym))
- sm_error("locking inconsistency. We assume "
- "'%s' is both locked and unlocked at the "
- "start.",
- tmp->name);
- } END_FOR_EACH_PTR(tmp);
-
- FOR_EACH_PTR(starts_locked, tmp) {
- check_returns_consistently(tmp, &locked);
- } END_FOR_EACH_PTR(tmp);
-
- FOR_EACH_PTR(starts_unlocked, tmp) {
- check_returns_consistently(tmp, &unlocked);
- } END_FOR_EACH_PTR(tmp);
+ db_param_locked_unlocked(expr, param, key, value, LOCK);
}
-static void clear_lists(void)
+static void db_param_unlocked(struct expression *expr, int param, char *key, char *value)
{
- struct locks_on_return *tmp;
+ db_param_locked_unlocked(expr, param, key, value, UNLOCK);
+}
- func_has_transition = FALSE;
+static void db_param_restore(struct expression *expr, int param, char *key, char *value)
+{
+ db_param_locked_unlocked(expr, param, key, value, RESTORE);
+}
- free_trackers_and_list(&starts_locked);
- free_trackers_and_list(&starts_unlocked);
+static int get_caller_param_lock_name(struct expression *call, struct sm_state *sm, const char **name)
+{
+ struct expression *arg;
+ char *arg_name;
+ int param;
+
+ param = 0;
+ FOR_EACH_PTR(call->args, arg) {
+ arg_name = sm_to_arg_name(arg, sm);
+ if (arg_name) {
+ *name = arg_name;
+ return param;
+ }
+ param++;
+ } END_FOR_EACH_PTR(arg);
- FOR_EACH_PTR(all_returns, tmp) {
- free_trackers_and_list(&tmp->locked);
- free_trackers_and_list(&tmp->unlocked);
- free(tmp);
- } END_FOR_EACH_PTR(tmp);
- __free_ptr_list((struct ptr_list **)&all_returns);
+ *name = sm->name;
+ return -2;
}
-static void match_func_end(struct symbol *sym)
+static void match_call_info(struct expression *expr)
{
- if (__inline_fn)
- return;
+ struct sm_state *sm;
+ const char *param_name;
+ int locked_type;
+ int param;
+
+ FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+ param = get_caller_param_lock_name(expr, sm, &param_name);
+ if (sm->state == &locked)
+ locked_type = LOCKED;
+ else if (sm->state == &half_locked ||
+ slist_has_state(sm->possible, &locked))
+ locked_type = HALF_LOCKED;
+ else
+ continue;
+ sql_insert_caller_info(expr, locked_type, param, param_name, "xxx type");
- if (func_has_transition)
- check_consistency(sym);
+ } END_FOR_EACH_SM(sm);
}
-static void match_after_func(struct symbol *sym)
+static void match_save_states(struct expression *expr)
{
- if (__inline_fn)
- return;
- clear_lists();
+ push_stree(&saved_stack, start_states);
+ start_states = NULL;
}
-static void register_lock(int index)
+static void match_restore_states(struct expression *expr)
{
- struct lock_info *lock = &lock_table[index];
- void *idx = INT_PTR(index);
+ start_states = pop_stree(&saved_stack);
+}
- if (lock->return_type == ret_non_zero) {
- return_implies_state(lock->function, 1, INT_MAX, &match_lock_held, idx);
- return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
- } else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) {
- add_function_assign_hook(lock->function, &match_returns_locked, idx);
- } else if (lock->return_type == ret_any) {
- add_function_hook(lock->function, &match_lock_unlock, idx);
- } else if (lock->return_type == ret_zero) {
- return_implies_state(lock->function, 0, 0, &match_lock_held, idx);
- return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx);
- } else if (lock->return_type == ret_one) {
- return_implies_state(lock->function, 1, 1, &match_lock_held, idx);
- return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
- }
+static void match_after_func(struct symbol *sym)
+{
+ free_stree(&start_states);
}
-static void load_table(struct lock_info *_lock_table, int size)
+static void match_dma_resv_lock_NULL(const char *fn, struct expression *call_expr,
+ struct expression *assign_expr, void *_index)
{
- int i;
+ struct expression *lock, *ctx;
+ char *lock_name;
+ struct symbol *sym;
- lock_table = _lock_table;
+ lock = get_argument_from_call_expr(call_expr->args, 0);
+ ctx = get_argument_from_call_expr(call_expr->args, 1);
+ if (!expr_is_zero(ctx))
+ return;
- for (i = 0; i < size; i++) {
- if (lock_table[i].action == LOCK)
- register_lock(i);
- else
- add_function_hook(lock_table[i].function, &match_lock_unlock, INT_PTR(i));
- }
+ lock_name = lock_to_name_sym(lock, &sym);
+ if (!lock_name || !sym)
+ goto free;
+ do_lock(lock_name, sym, NULL);
+free:
+ free_string(lock_name);
}
/* print_held_locks() is used in check_call_tree.c */
@@ -982,17 +1262,29 @@ void check_locking(int id)
{
my_id = id;
- if (option_project == PROJ_WINE)
- load_table(wine_lock_table, ARRAY_SIZE(wine_lock_table));
- else if (option_project == PROJ_KERNEL)
- load_table(kernel_lock_table, ARRAY_SIZE(kernel_lock_table));
- else
+ if (option_project != PROJ_KERNEL)
return;
+ load_table(lock_table);
+
+ set_dynamic_states(my_id);
add_unmatched_state_hook(my_id, &unmatched_state);
add_pre_merge_hook(my_id, &pre_merge_hook);
- add_split_return_callback(match_return);
+ add_merge_hook(my_id, &merge_func);
+ add_modification_hook(my_id, &reset);
+
add_hook(&match_func_end, END_FUNC_HOOK);
+
add_hook(&match_after_func, AFTER_FUNC_HOOK);
+ add_hook(&match_save_states, INLINE_FN_START);
+ add_hook(&match_restore_states, INLINE_FN_END);
+
+ add_hook(&match_call_info, FUNCTION_CALL_HOOK);
+
+ add_split_return_callback(match_return_info);
+ select_return_states_hook(LOCKED, &db_param_locked);
+ select_return_states_hook(UNLOCKED, &db_param_unlocked);
+ select_return_states_hook(LOCK_RESTORED, &db_param_restore);
+ return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL, 0);
}
diff --git a/usr/src/tools/smatch/src/check_param_mapper.c b/usr/src/tools/smatch/src/check_param_mapper.c
index b850f04dac..9eed4a95b2 100644
--- a/usr/src/tools/smatch/src/check_param_mapper.c
+++ b/usr/src/tools/smatch/src/check_param_mapper.c
@@ -23,7 +23,7 @@
* }
*
* Passing "bar" to foo() really means passing "bar" to baz();
- *
+ *
* In this case it would print:
* info: param_mapper 0 => bar 1
*
@@ -33,54 +33,6 @@
static int my_id;
-STATE(argument);
-
-static struct symbol *func_sym;
-
-static void delete(struct sm_state *sm, struct expression *mod_expr)
-{
- set_state(my_id, sm->name, sm->sym, &undefined);
-}
-
-static void match_function_def(struct symbol *sym)
-{
- struct symbol *arg;
-
- func_sym = sym;
- FOR_EACH_PTR(func_sym->ctype.base_type->arguments, arg) {
- if (!arg->ident) {
- continue;
- }
- set_state(my_id, arg->ident->name, arg, &argument);
- } END_FOR_EACH_PTR(arg);
-}
-
-static int get_arg_num(struct expression *expr)
-{
- struct smatch_state *state;
- struct symbol *arg;
- struct symbol *this_arg;
- int i;
-
- expr = strip_expr(expr);
- if (expr->type != EXPR_SYMBOL)
- return -1;
- this_arg = expr->symbol;
-
- state = get_state_expr(my_id, expr);
- if (!state || state != &argument)
- return -1;
-
- i = 0;
- FOR_EACH_PTR(func_sym->ctype.base_type->arguments, arg) {
- if (arg == this_arg)
- return i;
- i++;
- } END_FOR_EACH_PTR(arg);
-
- return -1;
-}
-
static void match_call(struct expression *expr)
{
struct expression *tmp;
@@ -93,22 +45,25 @@ static void match_call(struct expression *expr)
func = expr->fn->symbol_name->name;
- i = 0;
+ i = -1;
FOR_EACH_PTR(expr->args, tmp) {
- tmp = strip_expr(tmp);
- arg_num = get_arg_num(tmp);
- if (arg_num >= 0)
- sm_msg("info: param_mapper %d => %s %d", arg_num, func, i);
i++;
+ tmp = strip_expr(tmp);
+ if (tmp->type != EXPR_SYMBOL)
+ continue;
+ if (param_was_set(tmp))
+ continue;
+ arg_num = get_param_num_from_sym(tmp->symbol);
+ if (arg_num < 0)
+ continue;
+ sm_msg("info: param_mapper %d => %s %d", arg_num, func, i);
} END_FOR_EACH_PTR(tmp);
}
void check_param_mapper(int id)
{
- if (!option_param_mapper)
+ if (!option_info)
return;
my_id = id;
- add_modification_hook(my_id, &delete);
- add_hook(&match_function_def, FUNC_DEF_HOOK);
add_hook(&match_call, FUNCTION_CALL_HOOK);
}
diff --git a/usr/src/tools/smatch/src/smatch.c b/usr/src/tools/smatch/src/smatch.c
index e0fb1bd818..b6963a0016 100644
--- a/usr/src/tools/smatch/src/smatch.c
+++ b/usr/src/tools/smatch/src/smatch.c
@@ -34,7 +34,6 @@ int option_no_data = 0;
int option_spammy = 0;
int option_info = 0;
int option_full_path = 0;
-int option_param_mapper = 0;
int option_call_tree = 0;
int option_no_db = 0;
int option_enable = 0;
@@ -142,7 +141,6 @@ static void help(void)
printf("--spammy: print superfluous crap.\n");
printf("--info: print info used to fill smatch_data/.\n");
printf("--debug: print lots of debug output.\n");
- printf("--param-mapper: enable param_mapper output.\n");
printf("--no-data: do not use the /smatch_data/ directory.\n");
printf("--data=<dir>: overwrite path to default smatch data directory.\n");
printf("--full-path: print the full pathname.\n");
@@ -235,7 +233,6 @@ void parse_args(int *argcp, char ***argvp)
OPTION(no_data);
OPTION(two_passes);
OPTION(full_path);
- OPTION(param_mapper);
OPTION(call_tree);
OPTION(file_output);
OPTION(time);
diff --git a/usr/src/tools/smatch/src/smatch.h b/usr/src/tools/smatch/src/smatch.h
index a7ca2524a6..062dffc9d2 100644
--- a/usr/src/tools/smatch/src/smatch.h
+++ b/usr/src/tools/smatch/src/smatch.h
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <string.h>
#include <limits.h>
+#include <float.h>
#include <sys/time.h>
#include <sqlite3.h>
#include "lib.h"
@@ -37,6 +38,9 @@ typedef struct {
union {
long long value;
unsigned long long uvalue;
+ float fvalue;
+ double dvalue;
+ long double ldvalue;
};
} sval_t;
@@ -206,6 +210,7 @@ extern int final_pass;
extern struct symbol *cur_func_sym;
extern int option_debug;
extern int local_debug;
+extern int debug_db;
bool debug_implied(void);
extern int option_info;
extern int option_spammy;
@@ -240,7 +245,10 @@ extern const char *progname;
* sm_msg(): other message (please avoid using this)
*/
-#define sm_printf(msg...) do { if (final_pass || option_debug || local_debug) fprintf(sm_outfd, msg); } while (0)
+#define sm_printf(msg...) do { \
+ if (final_pass || option_debug || local_debug || debug_db) \
+ fprintf(sm_outfd, msg); \
+} while (0)
static inline void sm_prefix(void)
{
@@ -254,7 +262,7 @@ extern bool __silence_warnings_for_stmt;
#define sm_print_msg(type, msg...) \
do { \
print_implied_debug_msg(); \
- if (!final_pass && !option_debug && !local_debug) \
+ if (!final_pass && !option_debug && !local_debug && !debug_db) \
break; \
if (__silence_warnings_for_stmt && !option_debug && !local_debug) \
break; \
@@ -277,12 +285,6 @@ do { \
#define sm_msg(msg...) do { sm_print_msg(0, msg); } while (0)
-#define local_debug(msg...) \
-do { \
- if (local_debug) \
- sm_msg(msg); \
-} while (0)
-
extern char *implied_debug_msg;
static inline void print_implied_debug_msg(void)
{
@@ -297,6 +299,7 @@ static inline void print_implied_debug_msg(void)
}
#define sm_debug(msg...) do { if (option_debug) sm_printf(msg); } while (0)
+#define db_debug(msg...) do { if (option_debug || debug_db) sm_printf(msg); } while (0)
#define sm_info(msg...) do { \
if (option_debug || (option_info && final_pass)) { \
@@ -438,6 +441,7 @@ struct statement *get_prev_statement(void);
struct expression *get_last_expr_from_expression_stmt(struct expression *expr);
int get_param_num_from_sym(struct symbol *sym);
int get_param_num(struct expression *expr);
+struct symbol *get_param_sym_from_num(int num);
int ms_since(struct timeval *start);
int parent_is_gone_var_sym(const char *name, struct symbol *sym);
int parent_is_gone(struct expression *expr);
@@ -806,8 +810,6 @@ enum info_type {
RETURN_VALUE = 1005,
DEREFERENCE = 1006,
RANGE_CAP = 1007,
- LOCK_HELD = 1008,
- LOCK_RELEASED = 1009,
ABSOLUTE_LIMITS = 1010,
PARAM_ADD = 1012,
PARAM_FREED = 1013,
@@ -845,6 +847,10 @@ enum info_type {
NO_OVERFLOW_SIMPLE = 8019,
LOCKED = 8020,
UNLOCKED = 8021,
+ HALF_LOCKED = 9022,
+ LOCK_RESTORED = 9023,
+ KNOWN_LOCKED = 9024,
+ KNOWN_UNLOCKED = 9025,
SET_FS = 8022,
ATOMIC_INC = 8023,
ATOMIC_DEC = 8024,
@@ -887,6 +893,7 @@ const char *get_param_name(struct sm_state *sm);
const char *get_mtag_name_var_sym(const char *state_name, struct symbol *sym);
const char *get_mtag_name_expr(struct expression *expr);
char *get_data_info_name(struct expression *expr);
+char *sm_to_arg_name(struct expression *expr, struct sm_state *sm);
int is_recursive_member(const char *param_name);
char *escape_newlines(const char *str);
@@ -897,7 +904,7 @@ do { \
char sql_txt[1024]; \
\
sqlite3_snprintf(sizeof(sql_txt), sql_txt, sql); \
- sm_debug("debug: %s\n", sql_txt); \
+ db_debug("debug: %s\n", sql_txt); \
sql_exec(db, call_back, data, sql_txt); \
} while (0)
@@ -931,7 +938,7 @@ do { \
ignore ? "or ignore " : "", #table); \
p += snprintf(p, buf + sizeof(buf) - p, values); \
p += snprintf(p, buf + sizeof(buf) - p, ");"); \
- sm_debug("mem-db: %s\n", buf); \
+ db_debug("mem-db: %s\n", buf); \
rc = sqlite3_exec(_db, buf, NULL, NULL, &err); \
if (rc != SQLITE_OK) { \
sm_ierror("SQL error #2: %s", err); \
@@ -1007,7 +1014,6 @@ extern char *bin_dir;
extern char *data_dir;
extern int option_no_data;
extern int option_full_path;
-extern int option_param_mapper;
extern int option_call_tree;
extern int num_checks;
@@ -1062,6 +1068,7 @@ void __add_return_to_param_mapping(struct expression *assign, const char *return
char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym);
/* smatch_comparison.c */
+extern int comparison_id;
#define UNKNOWN_COMPARISON 0
#define IMPOSSIBLE_COMPARISON -1
struct compare_data {
@@ -1117,8 +1124,11 @@ sval_t *sval_alloc(sval_t sval);
sval_t *sval_alloc_permanent(sval_t sval);
sval_t sval_blank(struct expression *expr);
sval_t sval_type_val(struct symbol *type, long long val);
+sval_t sval_type_fval(struct symbol *type, long double fval);
sval_t sval_from_val(struct expression *expr, long long val);
+sval_t sval_from_fval(struct expression *expr, long double fval);
int sval_is_ptr(sval_t sval);
+bool sval_is_fp(sval_t sval);
int sval_unsigned(sval_t sval);
int sval_signed(sval_t sval);
int sval_bits(sval_t sval);
@@ -1171,6 +1181,10 @@ struct expression_list *get_conditions(struct expression *expr);
struct sm_state *stored_condition_implication_hook(struct expression *expr,
struct state_list **true_stack,
struct state_list **false_stack);
+/* smatch_parsed_conditions.c */
+struct sm_state *parsed_condition_implication_hook(struct expression *expr,
+ struct state_list **true_stack,
+ struct state_list **false_stack);
/* check_string_len.c */
int get_formatted_string_size(struct expression *call, int arg);
@@ -1272,6 +1286,14 @@ static inline bool type_is_ptr(struct symbol *type)
type->type == SYM_FN);
}
+static inline bool type_is_fp(struct symbol *type)
+{
+ return type &&
+ (type == &float_ctype ||
+ type == &double_ctype ||
+ type == &ldouble_ctype);
+}
+
static inline int type_bits(struct symbol *type)
{
if (!type)
@@ -1313,10 +1335,53 @@ static inline int sval_positive_bits(sval_t sval)
/*
* Returns -1 if one is smaller, 0 if they are the same and 1 if two is larger.
*/
+
+static inline int fp_cmp(sval_t one, sval_t two)
+{
+ struct symbol *type;
+
+ if (sval_is_fp(one) && sval_is_fp(two))
+ type = type_bits(one.type) > type_bits(two.type) ? one.type : two.type;
+ else if (sval_is_fp(one))
+ type = one.type;
+ else
+ type = two.type;
+
+ one = sval_cast(type, one);
+ two = sval_cast(type, two);
+
+ if (one.type == &float_ctype) {
+ if (one.fvalue < two.fvalue)
+ return -1;
+ if (one.fvalue == two.fvalue)
+ return 0;
+ return 1;
+ }
+ if (one.type == &double_ctype) {
+ if (one.dvalue < two.dvalue)
+ return -1;
+ if (one.dvalue == two.dvalue)
+ return 0;
+ return 1;
+ }
+ if (one.type == &ldouble_ctype) {
+ if (one.ldvalue < two.ldvalue)
+ return -1;
+ if (one.ldvalue == two.ldvalue)
+ return 0;
+ return 1;
+ }
+ sm_perror("bad type in fp_cmp(): %s", type_to_str(type));
+ return 1;
+}
+
static inline int sval_cmp(sval_t one, sval_t two)
{
struct symbol *type;
+ if (sval_is_fp(one) || sval_is_fp(two))
+ return fp_cmp(one, two);
+
type = one.type;
if (sval_positive_bits(two) > sval_positive_bits(one))
type = two.type;
diff --git a/usr/src/tools/smatch/src/smatch_address.c b/usr/src/tools/smatch/src/smatch_address.c
index 539492a023..86f50be259 100644
--- a/usr/src/tools/smatch/src/smatch_address.c
+++ b/usr/src/tools/smatch/src/smatch_address.c
@@ -119,6 +119,11 @@ int get_member_offset_from_deref(struct expression *expr)
struct ident *member;
int offset;
+ /*
+ * FIXME: This doesn't handle foo.u.bar correctly.
+ *
+ */
+
if (expr->type != EXPR_DEREF) /* hopefully, this doesn't happen */
return -1;
@@ -202,25 +207,66 @@ static struct range_list *where_allocated_rl(struct symbol *sym)
if (!sym)
return NULL;
+ /* This should just be the mtag if it's not on the stack */
return alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
}
+static bool handle_fn_address(struct expression *expr, struct range_list **rl)
+{
+ struct symbol *type;
+
+ if (expr->type == EXPR_PREOP && expr->op == '&')
+ expr = strip_expr(expr->unop);
+
+ if (expr->type != EXPR_SYMBOL)
+ return false;
+
+ type = get_type(expr);
+ if (!type || type->type != SYM_FN)
+ return false;
+
+ *rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
+ return true;
+}
+
int get_address_rl(struct expression *expr, struct range_list **rl)
{
struct expression *unop;
+ /*
+ * Ugh... This function is bad. It doesn't work where it's supposed to
+ * and it does more than it really should. It shouldn't handle string
+ * literals I think...
+ *
+ * There are several complications. For arrays and functions the "&foo"
+ * "foo" are equivalent. But the problem is that we're also passing in
+ * foo->array[] and foo->fn.
+ *
+ * Then, when we have foo->bar.baz.one.two; that needs to be handled
+ * correctly but right now, it is not.
+ *
+ */
+
expr = strip_expr(expr);
if (!expr)
return 0;
- if (expr->type == EXPR_STRING) {
- *rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
+ /*
+ * For functions &fn and fn are equivalent. I don't know if this is
+ * really the right place to handle it, but let's just get it out of the
+ * way for now.
+ *
+ */
+ if (handle_fn_address(expr, rl))
return 1;
- }
- if (expr->type == EXPR_PREOP && expr->op == '&')
+ /*
+ * For arrays, &foo->array and foo->array are equivalent.
+ *
+ */
+ if (expr->type == EXPR_PREOP && expr->op == '&') {
expr = strip_expr(expr->unop);
- else {
+ } else {
struct symbol *type;
type = get_type(expr);
diff --git a/usr/src/tools/smatch/src/smatch_buf_size.c b/usr/src/tools/smatch/src/smatch_buf_size.c
index bd10f7e505..1a2dd87d01 100644
--- a/usr/src/tools/smatch/src/smatch_buf_size.c
+++ b/usr/src/tools/smatch/src/smatch_buf_size.c
@@ -910,6 +910,7 @@ void register_buf_size(int id)
add_allocation_function("kzalloc_node", &match_alloc, 0);
add_allocation_function("vmalloc", &match_alloc, 0);
add_allocation_function("__vmalloc", &match_alloc, 0);
+ add_allocation_function("kvmalloc", &match_alloc, 0);
add_allocation_function("kcalloc", &match_calloc, 0);
add_allocation_function("kmalloc_array", &match_calloc, 0);
add_allocation_function("drm_malloc_ab", &match_calloc, 0);
diff --git a/usr/src/tools/smatch/src/smatch_comparison.c b/usr/src/tools/smatch/src/smatch_comparison.c
index cd31f09b1b..78724c2339 100644
--- a/usr/src/tools/smatch/src/smatch_comparison.c
+++ b/usr/src/tools/smatch/src/smatch_comparison.c
@@ -32,7 +32,7 @@
#include "smatch_extra.h"
#include "smatch_slist.h"
-static int compare_id;
+int comparison_id;
static int link_id;
static int inc_dec_id;
static int inc_dec_link_id;
@@ -542,6 +542,7 @@ static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
int extra, new;
static bool in_recurse;
+ // FIXME. No data is useless
if (!data)
return;
@@ -556,7 +557,8 @@ static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
if (new == data->comparison)
return;
- set_state(compare_id, cur->name, NULL,
+ // FIXME: we should always preserve implications
+ set_state(comparison_id, cur->name, NULL,
alloc_compare_state(data->left, data->left_var, data->left_vsl,
new,
data->right, data->right_var, data->right_vsl));
@@ -624,7 +626,7 @@ static void save_start_states(struct statement *stmt)
NULL, param->ident->name, left_vsl,
SPECIAL_EQUAL,
NULL, alloc_sname(orig), right_vsl);
- set_state(compare_id, state_name, NULL, state);
+ set_state(comparison_id, state_name, NULL, state);
link = alloc_sname(state_name);
links = NULL;
@@ -675,7 +677,7 @@ static void match_inc(struct sm_state *sm, bool preserve)
links = sm->state->data;
FOR_EACH_PTR(links, tmp) {
- state = get_state(compare_id, tmp, NULL);
+ state = get_state(comparison_id, tmp, NULL);
if (!state)
continue;
data = state->data;
@@ -701,7 +703,7 @@ static void match_inc(struct sm_state *sm, bool preserve)
data->left, data->left_var, data->left_vsl,
flip ? '<' : '>',
data->right, data->right_var, data->right_vsl);
- set_state(compare_id, tmp, NULL, new);
+ set_state(comparison_id, tmp, NULL, new);
break;
case '<':
case SPECIAL_UNSIGNED_LT:
@@ -709,14 +711,14 @@ static void match_inc(struct sm_state *sm, bool preserve)
data->left, data->left_var, data->left_vsl,
flip ? SPECIAL_GTE : SPECIAL_LTE,
data->right, data->right_var, data->right_vsl);
- set_state(compare_id, tmp, NULL, new);
+ set_state(comparison_id, tmp, NULL, new);
break;
default:
new = alloc_compare_state(
data->left, data->left_var, data->left_vsl,
UNKNOWN_COMPARISON,
data->right, data->right_var, data->right_vsl);
- set_state(compare_id, tmp, NULL, new);
+ set_state(comparison_id, tmp, NULL, new);
}
} END_FOR_EACH_PTR(tmp);
}
@@ -733,7 +735,7 @@ static void match_dec(struct sm_state *sm, bool preserve)
struct compare_data *data;
struct smatch_state *new;
- state = get_state(compare_id, tmp, NULL);
+ state = get_state(comparison_id, tmp, NULL);
if (!state || !state->data)
continue;
@@ -752,7 +754,7 @@ static void match_dec(struct sm_state *sm, bool preserve)
data->left, data->left_var, data->left_vsl,
'<',
data->right, data->right_var, data->right_vsl);
- set_state(compare_id, tmp, NULL, new);
+ set_state(comparison_id, tmp, NULL, new);
break;
}
default:
@@ -760,7 +762,7 @@ static void match_dec(struct sm_state *sm, bool preserve)
data->left, data->left_var, data->left_vsl,
UNKNOWN_COMPARISON,
data->right, data->right_var, data->right_vsl);
- set_state(compare_id, tmp, NULL, new);
+ set_state(comparison_id, tmp, NULL, new);
}
} END_FOR_EACH_PTR(tmp);
}
@@ -775,7 +777,7 @@ static void reset_sm(struct sm_state *sm)
FOR_EACH_PTR(links, tmp) {
struct smatch_state *old, *new;
- old = get_state(compare_id, tmp, NULL);
+ old = get_state(comparison_id, tmp, NULL);
if (!old || !old->data) {
new = &undefined;
} else {
@@ -786,7 +788,7 @@ static void reset_sm(struct sm_state *sm)
UNKNOWN_COMPARISON,
data->right, data->right_var, data->right_vsl);
}
- set_state(compare_id, tmp, NULL, new);
+ set_state(comparison_id, tmp, NULL, new);
} END_FOR_EACH_PTR(tmp);
set_state(link_id, sm->name, sm->sym, &undefined);
}
@@ -988,7 +990,7 @@ static int get_orig_comparison(struct stree *pre_stree, const char *left, const
}
snprintf(state_name, sizeof(state_name), "%s vs %s", left, right);
- state = get_state_stree(pre_stree, compare_id, state_name, NULL);
+ state = get_state_stree(pre_stree, comparison_id, state_name, NULL);
if (!state || !state->data)
return 0;
data = state->data;
@@ -1041,7 +1043,7 @@ static void update_tf_links(struct stree *pre_stree,
struct var_sym *vs;
FOR_EACH_PTR(links, tmp) {
- state = get_state_stree(pre_stree, compare_id, tmp, NULL);
+ state = get_state_stree(pre_stree, comparison_id, tmp, NULL);
if (!state || !state->data)
continue;
data = state->data;
@@ -1100,7 +1102,7 @@ static void update_tf_links(struct stree *pre_stree,
false_state = NULL;
snprintf(state_name, sizeof(state_name), "%s vs %s", left_var, right_var);
- set_true_false_states(compare_id, state_name, NULL, true_state, false_state);
+ set_true_false_states(comparison_id, state_name, NULL, true_state, false_state);
FOR_EACH_PTR(left_vsl, vs) {
save_link_var_sym(vs->var, vs->sym, state_name);
} END_FOR_EACH_PTR(vs);
@@ -1199,7 +1201,8 @@ static void handle_for_loops(struct expression *expr, char *state_name, struct s
SPECIAL_EQUAL,
data->right, data->right_var, data->right_vsl);
- set_true_false_states(compare_id, state_name, NULL, NULL, false_state);
+ // FIXME: This doesn't handle links correct so it doesn't set "param orig"
+ set_true_false_states(comparison_id, state_name, NULL, NULL, false_state);
}
static int is_plus_one(struct expression *expr)
@@ -1340,7 +1343,7 @@ static void handle_comparison(struct expression *left_expr, int op, struct expre
update_tf_data(pre_stree, left_expr, left, left_vsl, right_expr, right, right_vsl, op, false_op);
free_stree(&pre_stree);
- set_true_false_states(compare_id, state_name, NULL, true_state, false_state);
+ set_true_false_states(comparison_id, state_name, NULL, true_state, false_state);
__compare_param_limit_hook(left_expr, right_expr, state_name, true_state, false_state);
save_link(left_expr, state_name);
save_link(right_expr, state_name);
@@ -1442,7 +1445,7 @@ static void add_comparison_var_sym(
comparison,
right_expr, right_name, right_vsl);
- set_state(compare_id, state_name, NULL, state);
+ set_state(comparison_id, state_name, NULL, state);
FOR_EACH_PTR(left_vsl, vs) {
save_link_var_sym(vs->var, vs->sym, state_name);
@@ -1492,7 +1495,7 @@ static void add_comparison(struct expression *left, int comparison, struct expre
comparison,
right, right_name, right_vsl);
- set_state(compare_id, state_name, NULL, state);
+ set_state(comparison_id, state_name, NULL, state);
save_link(left, state_name);
save_link(right, state_name);
@@ -1609,7 +1612,7 @@ static void copy_comparisons(struct expression *left, struct expression *right)
links = state->data;
FOR_EACH_PTR(links, tmp) {
- state = get_state(compare_id, tmp, NULL);
+ state = get_state(comparison_id, tmp, NULL);
if (!state || !state->data)
continue;
data = state->data;
@@ -1678,7 +1681,7 @@ int get_comparison_strings(const char *one, const char *two)
}
snprintf(buf, sizeof(buf), "%s vs %s", one, two);
- state = get_state(compare_id, buf, NULL);
+ state = get_state(comparison_id, buf, NULL);
if (state)
ret = state_to_comparison(state);
@@ -1784,7 +1787,7 @@ int possible_comparison(struct expression *a, int comparison, struct expression
}
snprintf(buf, sizeof(buf), "%s vs %s", one, two);
- sm = get_sm_state(compare_id, buf, NULL);
+ sm = get_sm_state(comparison_id, buf, NULL);
if (!sm)
goto free;
@@ -1825,7 +1828,7 @@ struct state_list *get_all_comparisons(struct expression *expr)
links = state->data;
FOR_EACH_PTR(links, tmp) {
- sm = get_sm_state(compare_id, tmp, NULL);
+ sm = get_sm_state(comparison_id, tmp, NULL);
if (!sm)
continue;
// FIXME have to compare name with vsl
@@ -1849,7 +1852,7 @@ struct state_list *get_all_possible_equal_comparisons(struct expression *expr)
links = state->data;
FOR_EACH_PTR(links, tmp) {
- sm = get_sm_state(compare_id, tmp, NULL);
+ sm = get_sm_state(comparison_id, tmp, NULL);
if (!sm)
continue;
if (!strchr(sm->state->name, '='))
@@ -1879,7 +1882,7 @@ struct state_list *get_all_possible_not_equal_comparisons(struct expression *exp
links = state->data;
FOR_EACH_PTR(links, link) {
- sm = get_sm_state(compare_id, link, NULL);
+ sm = get_sm_state(comparison_id, link, NULL);
if (!sm)
continue;
FOR_EACH_PTR(sm->possible, possible) {
@@ -1924,7 +1927,7 @@ static void update_links_from_call(struct expression *left,
links = state->data;
FOR_EACH_PTR(links, tmp) {
- state = get_state(compare_id, tmp, NULL);
+ state = get_state(comparison_id, tmp, NULL);
if (!state || !state->data)
continue;
data = state->data;
@@ -2173,7 +2176,7 @@ static void match_call_info(struct expression *expr)
if (strstr(link, " orig"))
continue;
- sm = get_sm_state(compare_id, link, NULL);
+ sm = get_sm_state(comparison_id, link, NULL);
if (!sm)
continue;
data = sm->state->data;
@@ -2228,7 +2231,7 @@ static void struct_member_callback(struct expression *call, int param, char *pri
links = link_sm->state->data;
FOR_EACH_PTR(links, link) {
- compare_sm = get_sm_state(compare_id, link, NULL);
+ compare_sm = get_sm_state(comparison_id, link, NULL);
if (!compare_sm)
continue;
data = compare_sm->state->data;
@@ -2321,7 +2324,7 @@ static void print_return_comparison(int return_id, char *return_ranges, struct e
continue;
links = tmp->state->data;
FOR_EACH_PTR(links, link) {
- sm = get_sm_state(compare_id, link, NULL);
+ sm = get_sm_state(comparison_id, link, NULL);
if (!sm)
continue;
data = sm->state->data;
@@ -2539,7 +2542,7 @@ int param_compare_limit_is_impossible(struct expression *expr, int left_param, c
goto free;
snprintf(buf, sizeof(buf), "%s vs %s", left_name, right_name);
- state = get_state(compare_id, buf, NULL);
+ state = get_state(comparison_id, buf, NULL);
if (!state)
goto free;
state_op = state_to_comparison(state);
@@ -2573,7 +2576,7 @@ int impossibly_high_comparison(struct expression *expr)
links = link_state->data;
FOR_EACH_PTR(links, link) {
- sm = get_sm_state(compare_id, link, NULL);
+ sm = get_sm_state(comparison_id, link, NULL);
if (!sm)
continue;
data = sm->state->data;
@@ -2595,12 +2598,12 @@ static void free_data(struct symbol *sym)
void register_comparison(int id)
{
- compare_id = id;
- set_dynamic_states(compare_id);
+ comparison_id = id;
+ set_dynamic_states(comparison_id);
add_hook(&save_start_states, AFTER_DEF_HOOK);
- add_unmatched_state_hook(compare_id, unmatched_comparison);
- add_pre_merge_hook(compare_id, &pre_merge_hook);
- add_merge_hook(compare_id, &merge_compare_states);
+ add_unmatched_state_hook(comparison_id, unmatched_comparison);
+ add_pre_merge_hook(comparison_id, &pre_merge_hook);
+ add_merge_hook(comparison_id, &merge_compare_states);
add_hook(&free_data, AFTER_FUNC_HOOK);
add_hook(&match_call_info, FUNCTION_CALL_HOOK);
add_split_return_callback(&print_return_comparison);
@@ -2639,9 +2642,60 @@ void register_comparison_inc_dec_links(int id)
set_up_link_functions(inc_dec_id, inc_dec_link_id);
}
+static struct sm_state *clone_partial_sm(struct sm_state *sm, int comparison)
+{
+ struct compare_data *data;
+ struct sm_state *clone;
+ struct stree *stree;
+
+ data = sm->state->data;
+
+ clone = clone_sm(sm);
+ clone->state = alloc_compare_state(data->left, data->left_var, data->left_vsl,
+ comparison,
+ data->right, data->right_var, data->right_vsl);
+ free_slist(&clone->possible);
+ add_possible_sm(clone, clone);
+
+ stree = clone_stree(sm->pool);
+ overwrite_sm_state_stree(&stree, clone);
+ clone->pool = stree;
+
+ return clone;
+}
+
+static void create_fake_history(struct sm_state *sm, int op,
+ struct state_list **true_stack,
+ struct state_list **false_stack)
+{
+ struct sm_state *true_sm, *false_sm;
+ struct compare_data *data;
+ int true_comparison;
+ int false_comparison;
+
+ data = sm->state->data;
+
+ if (is_merged(sm) || sm->left || sm->right)
+ return;
+
+ true_comparison = comparison_intersection(data->comparison, op);
+ false_comparison = comparison_intersection(data->comparison, negate_comparison(op));
+
+ true_sm = clone_partial_sm(sm, true_comparison);
+ false_sm = clone_partial_sm(sm, false_comparison);
+
+ sm->merged = 1;
+ sm->left = true_sm;
+ sm->right = false_sm;
+
+ add_ptr_list(true_stack, true_sm);
+ add_ptr_list(false_stack, false_sm);
+}
+
static void filter_by_sm(struct sm_state *sm, int op,
struct state_list **true_stack,
- struct state_list **false_stack)
+ struct state_list **false_stack,
+ bool *useful)
{
struct compare_data *data;
int is_true = 0;
@@ -2650,7 +2704,7 @@ static void filter_by_sm(struct sm_state *sm, int op,
if (!sm)
return;
data = sm->state->data;
- if (!data || data->comparison == UNKNOWN_COMPARISON)
+ if (!data)
goto split;
if (data->comparison == IMPOSSIBLE_COMPARISON)
return;
@@ -2667,6 +2721,11 @@ static void filter_by_sm(struct sm_state *sm, int op,
if (data->comparison == comparison_intersection(data->comparison, negate_comparison(op)))
is_false = 1;
+ if (!is_true && !is_false && !is_merged(sm)) {
+ create_fake_history(sm, op, true_stack, false_stack);
+ return;
+ }
+
if (debug_implied()) {
sm_msg("%s: %s: op = '%s' negated '%s'. true_intersect = '%s' false_insersect = '%s' sm = '%s'",
__func__,
@@ -2678,13 +2737,14 @@ static void filter_by_sm(struct sm_state *sm, int op,
show_sm(sm));
}
+ *useful = true;
if (is_true)
add_ptr_list(true_stack, sm);
if (is_false)
add_ptr_list(false_stack, sm);
split:
- filter_by_sm(sm->left, op, true_stack, false_stack);
- filter_by_sm(sm->right, op, true_stack, false_stack);
+ filter_by_sm(sm->left, op, true_stack, false_stack, useful);
+ filter_by_sm(sm->right, op, true_stack, false_stack, useful);
}
struct sm_state *comparison_implication_hook(struct expression *expr,
@@ -2695,6 +2755,7 @@ struct sm_state *comparison_implication_hook(struct expression *expr,
char *left, *right;
int op;
static char buf[256];
+ bool useful = false;
if (expr->type != EXPR_COMPARE)
return NULL;
@@ -2718,14 +2779,14 @@ struct sm_state *comparison_implication_hook(struct expression *expr,
}
snprintf(buf, sizeof(buf), "%s vs %s", left, right);
- sm = get_sm_state(compare_id, buf, NULL);
+ sm = get_sm_state(comparison_id, buf, NULL);
if (!sm)
return NULL;
if (!sm->merged)
return NULL;
- filter_by_sm(sm, op, true_stack, false_stack);
- if (!*true_stack && !*false_stack)
+ filter_by_sm(sm, op, true_stack, false_stack, &useful);
+ if (!useful)
return NULL;
if (debug_implied())
diff --git a/usr/src/tools/smatch/src/smatch_container_of.c b/usr/src/tools/smatch/src/smatch_container_of.c
index a832aee15b..bd9039cf75 100644
--- a/usr/src/tools/smatch/src/smatch_container_of.c
+++ b/usr/src/tools/smatch/src/smatch_container_of.c
@@ -22,11 +22,6 @@
static int my_id;
static int param_id;
-static struct stree *used_stree;
-static struct stree_stack *saved_stack;
-
-STATE(used);
-
int get_param_from_container_of(struct expression *expr)
{
struct expression *param_expr;
@@ -84,121 +79,6 @@ int get_offset_from_container_of(struct expression *expr)
return sval.value;
}
-static int get_container_arg(struct symbol *sym)
-{
- struct expression *__mptr;
- int param;
-
- if (!sym || !sym->ident)
- return -1;
-
- __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
- param = get_param_from_container_of(__mptr);
-
- return param;
-}
-
-static int get_container_offset(struct symbol *sym)
-{
- struct expression *__mptr;
- int offset;
-
- if (!sym || !sym->ident)
- return -1;
-
- __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
- offset = get_offset_from_container_of(__mptr);
-
- return offset;
-}
-
-static char *get_container_name_sm(struct sm_state *sm, int offset)
-{
- static char buf[256];
- const char *name;
-
- name = get_param_name(sm);
- if (!name)
- return NULL;
-
- if (name[0] == '$')
- snprintf(buf, sizeof(buf), "$(-%d)%s", offset, name + 1);
- else if (name[0] == '*' || name[1] == '$')
- snprintf(buf, sizeof(buf), "*$(-%d)%s", offset, name + 2);
- else
- return NULL;
-
- return buf;
-}
-
-static void get_state_hook(int owner, const char *name, struct symbol *sym)
-{
- int arg;
-
- if (!option_info)
- return;
- if (__in_fake_assign)
- return;
-
- arg = get_container_arg(sym);
- if (arg >= 0)
- set_state_stree(&used_stree, my_id, name, sym, &used);
-}
-
-static void set_param_used(struct expression *call, struct expression *arg, char *key, char *unused)
-{
- struct symbol *sym;
- char *name;
- int arg_nr;
-
- name = get_variable_from_key(arg, key, &sym);
- if (!name || !sym)
- goto free;
-
- arg_nr = get_container_arg(sym);
- if (arg_nr >= 0)
- set_state(my_id, name, sym, &used);
-free:
- free_string(name);
-}
-
-static void process_states(void)
-{
- struct sm_state *tmp;
- int arg, offset;
- const char *name;
-
- FOR_EACH_SM(used_stree, tmp) {
- arg = get_container_arg(tmp->sym);
- offset = get_container_offset(tmp->sym);
- if (arg < 0 || offset < 0)
- continue;
- name = get_container_name_sm(tmp, offset);
- if (!name)
- continue;
- sql_insert_return_implies(CONTAINER, arg, name, "");
- } END_FOR_EACH_SM(tmp);
-
- free_stree(&used_stree);
-}
-
-static void match_function_def(struct symbol *sym)
-{
- free_stree(&used_stree);
-}
-
-static void match_save_states(struct expression *expr)
-{
- push_stree(&saved_stack, used_stree);
- used_stree = NULL;
-}
-
-static void match_restore_states(struct expression *expr)
-{
- free_stree(&used_stree);
- used_stree = pop_stree(&saved_stack);
-}
-
static void print_returns_container_of(int return_id, char *return_ranges, struct expression *expr)
{
int offset;
@@ -221,34 +101,6 @@ static void print_returns_container_of(int return_id, char *return_ranges, struc
key, value);
}
-static void returns_container_of(struct expression *expr, int param, char *key, char *value)
-{
- struct expression *call, *arg;
- int offset;
- char buf[64];
-
- if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
- return;
- call = strip_expr(expr->right);
- if (call->type != EXPR_CALL)
- return;
- if (param != -1)
- return;
- param = atoi(key);
- offset = atoi(value);
-
- arg = get_argument_from_call_expr(call->args, param);
- if (!arg)
- return;
- if (arg->type != EXPR_SYMBOL)
- return;
- param = get_param_num(arg);
- if (param < 0)
- return;
- snprintf(buf, sizeof(buf), "$(%d)", offset);
- sql_insert_return_implies(CONTAINER, param, buf, "");
-}
-
static int get_deref_count(struct expression *expr)
{
int cnt = 0;
@@ -299,21 +151,21 @@ static int partial_deref_to_offset_str(struct expression *expr, int cnt, char op
return n;
}
-static char *get_shared_str(struct expression *container, struct expression *expr)
+static char *get_shared_str(struct expression *expr, struct expression *container)
{
struct expression *one, *two;
- int cont, exp, min, ret, n;
+ int exp, cont, min, ret, n;
static char buf[48];
- cont = get_deref_count(container);
exp = get_deref_count(expr);
- if (cont < 0 || exp < 0)
+ cont = get_deref_count(container);
+ if (exp < 0 || cont < 0)
return NULL;
- min = (cont < exp) ? cont : exp;
+ min = (exp < cont) ? exp : cont;
while (min >= 0) {
- one = get_partial_deref(container, cont - min);
- two = get_partial_deref(expr, exp - min);
+ one = get_partial_deref(expr, exp - min);
+ two = get_partial_deref(container, cont - min);
if (expr_equiv(one, two))
goto found;
min--;
@@ -322,11 +174,11 @@ static char *get_shared_str(struct expression *container, struct expression *exp
return NULL;
found:
- ret = partial_deref_to_offset_str(container, cont - min, '-', buf, sizeof(buf));
+ ret = partial_deref_to_offset_str(expr, exp - min, '-', buf, sizeof(buf));
if (ret < 0)
return NULL;
n = ret;
- ret = partial_deref_to_offset_str(expr, exp - min, '+', buf + ret, sizeof(buf) - ret);
+ ret = partial_deref_to_offset_str(container, cont - min, '+', buf + ret, sizeof(buf) - ret);
if (ret < 0)
return NULL;
n += ret;
@@ -336,50 +188,90 @@ found:
return buf;
}
+static char *get_stored_container_name(struct expression *container,
+ struct expression *expr)
+{
+ struct smatch_state *state;
+ static char buf[64];
+ char *p;
+ int param;
+
+ if (!container || container->type != EXPR_SYMBOL)
+ return NULL;
+ if (!expr || expr->type != EXPR_SYMBOL)
+ return NULL;
+ state = get_state_expr(param_id, expr);
+ if (!state)
+ return NULL;
+
+ snprintf(buf, sizeof(buf), "%s", state->name);
+ p = strchr(buf, '|');
+ if (!p)
+ return NULL;
+ *p = '\0';
+ param = atoi(p + 2);
+ if (get_param_sym_from_num(param) == container->symbol)
+ return buf;
+ return NULL;
+}
+
char *get_container_name(struct expression *container, struct expression *expr)
{
struct symbol *container_sym, *sym;
struct expression *tmp;
static char buf[64];
- char *shared;
+ char *ret, *shared;
bool star;
int cnt;
- container_sym = expr_to_sym(container);
+ expr = strip_expr(expr);
+ container = strip_expr(container);
+
+ ret = get_stored_container_name(container, expr);
+ if (ret)
+ return ret;
+
sym = expr_to_sym(expr);
- if (container_sym && container_sym == sym)
+ container_sym = expr_to_sym(container);
+ if (sym && sym == container_sym)
goto found;
cnt = 0;
- while ((tmp = get_assigned_expr(expr))) {
- expr = tmp;
+ while ((tmp = get_assigned_expr(container))) {
+ container = strip_expr(tmp);
if (cnt++ > 3)
break;
}
cnt = 0;
- while ((tmp = get_assigned_expr(container))) {
- container = tmp;
+ while ((tmp = get_assigned_expr(expr))) {
+ expr = strip_expr(tmp);
if (cnt++ > 3)
break;
}
found:
- expr = strip_expr(expr);
- star = true;
- if (expr->type == EXPR_PREOP && expr->op == '&') {
- expr = strip_expr(expr->unop);
+
+ if (container->type == EXPR_DEREF)
+ star = true;
+ else
star = false;
- }
- container_sym = expr_to_sym(container);
- if (!container_sym)
- return NULL;
+ if (container->type == EXPR_PREOP && container->op == '&')
+ container = strip_expr(container->unop);
+ if (expr->type == EXPR_PREOP && expr->op == '&')
+ expr = strip_expr(expr->unop);
+
sym = expr_to_sym(expr);
- if (!sym || container_sym != sym)
+ if (!sym)
+ return NULL;
+ container_sym = expr_to_sym(container);
+ if (!container_sym || sym != container_sym)
return NULL;
- shared = get_shared_str(container, expr);
+ shared = get_shared_str(expr, container);
+ if (!shared)
+ return NULL;
if (star)
snprintf(buf, sizeof(buf), "*(%s)", shared);
else
@@ -388,11 +280,31 @@ found:
return buf;
}
+static bool is_fn_ptr(struct expression *expr)
+{
+ struct symbol *type;
+
+ if (!expr)
+ return false;
+ if (expr->type != EXPR_SYMBOL && expr->type != EXPR_DEREF)
+ return false;
+
+ type = get_type(expr);
+ if (!type || type->type != SYM_PTR)
+ return false;
+ type = get_real_base_type(type);
+ if (!type || type->type != SYM_FN)
+ return false;
+ return true;
+}
+
static void match_call(struct expression *call)
{
- struct expression *fn, *arg;
+ struct expression *fn, *arg, *tmp;
+ bool found = false;
+ int fn_param, param;
+ char buf[32];
char *name;
- int param;
/*
* We're trying to link the function with the parameter. There are a
@@ -414,17 +326,47 @@ static void match_call(struct expression *call)
FOR_EACH_PTR(call->args, arg) {
param++;
- name = get_container_name(fn, arg);
+ name = get_container_name(arg, fn);
if (!name)
continue;
+ found = true;
sql_insert_caller_info(call, CONTAINER, param, name, "$(-1)");
} END_FOR_EACH_PTR(arg);
+
+ if (found)
+ return;
+
+ fn_param = -1;
+ FOR_EACH_PTR(call->args, arg) {
+ fn_param++;
+ if (!is_fn_ptr(arg))
+ continue;
+ param = -1;
+ FOR_EACH_PTR(call->args, tmp) {
+ param++;
+
+ /* the function isn't it's own container */
+ if (arg == tmp)
+ continue;
+
+ name = get_container_name(tmp, arg);
+ if (!name)
+ continue;
+
+ snprintf(buf, sizeof(buf), "$%d", param);
+ sql_insert_caller_info(call, CONTAINER, fn_param, name, buf);
+ return;
+ } END_FOR_EACH_PTR(tmp);
+ } END_FOR_EACH_PTR(arg);
}
static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
{
- set_state(param_id, name, sym, alloc_state_str(key));
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "%s|%s", key, value);
+ set_state(param_id, name, sym, alloc_state_str(buf));
}
struct db_info {
@@ -495,7 +437,7 @@ static const char *get_name_from_offset(struct symbol *arg, int offset)
}
member = get_member_from_offset(arg, offset);
- if (!member)
+ if (!member || !member->ident)
return NULL;
snprintf(fullname, sizeof(fullname), "%s->%s", name, member->ident->name);
@@ -605,16 +547,27 @@ static void load_container_data(struct symbol *arg, const char *info)
{
mtag_t cur_tag, container_tag, arg_tag;
int container_offset, arg_offset;
- char *p = (char *)info;
struct sm_state *sm;
struct stree *stree;
+ char *p, *cont;
+ char copy[64];
bool star = 0;
+ snprintf(copy, sizeof(copy), "%s", info);
+ p = strchr(copy, '|');
+ if (!p)
+ return;
+ *p = '\0';
+ cont = p + 1;
+ p = copy;
if (p[0] == '*') {
star = 1;
p += 2;
}
+ if (strcmp(cont, "$(-1)") != 0)
+ return;
+
if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
return;
@@ -675,19 +628,7 @@ void register_container_of(int id)
{
my_id = id;
- add_hook(&match_function_def, FUNC_DEF_HOOK);
-
- add_get_state_hook(&get_state_hook);
-
- add_hook(&match_save_states, INLINE_FN_START);
- add_hook(&match_restore_states, INLINE_FN_END);
-
- select_return_implies_hook(CONTAINER, &set_param_used);
- all_return_states_hook(&process_states);
-
add_split_return_callback(&print_returns_container_of);
- select_return_states_hook(CONTAINER, &returns_container_of);
-
add_hook(&match_call, FUNCTION_CALL_HOOK);
}
diff --git a/usr/src/tools/smatch/src/smatch_data/db/create_db.sh b/usr/src/tools/smatch/src/smatch_data/db/create_db.sh
index c75c2aad4c..313ba59133 100755
--- a/usr/src/tools/smatch/src/smatch_data/db/create_db.sh
+++ b/usr/src/tools/smatch/src/smatch_data/db/create_db.sh
@@ -52,5 +52,6 @@ ${bin_dir}/mark_function_ptrs_searchable.pl $db_file
echo "delete from function_ptr where rowid not in (select min(rowid) from function_ptr group by file, function, ptr, searchable);" | sqlite3 $db_file
${bin_dir}/apply_return_fixes.sh -p=${PROJ} $db_file
+${bin_dir}/insert_manual_states.pl ${PROJ} $db_file
mv $db_file smatch_db.sqlite
diff --git a/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh b/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh
index 7eb910eb3f..601055ee0c 100755
--- a/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh
+++ b/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh
@@ -180,6 +180,11 @@ delete from return_states where function = 'ata_dev_next' and type = 103;
/* The problem is that parsing big function pointers is hard. */
delete from return_states where function = 'vfs_get_tree' and type = 1024;
+/* Locking stuff goes here. */
+update return_states set parameter = -1, key = '\$' where function = 'ipmi_ssif_lock_cond' and type = 8020 and parameter = 1;
+update return_states set parameter = 1, key = '\$->tree->tree_lock' where function = 'hfs_find_init' and type = 8020 and parameter = 0;
+delete from return_states where function = '__oom_kill_process' and type = 8021;
+
EOF
# fixme: this is totally broken
diff --git a/usr/src/tools/smatch/src/smatch_data/db/insert_manual_states.pl b/usr/src/tools/smatch/src/smatch_data/db/insert_manual_states.pl
new file mode 100755
index 0000000000..6144af16b6
--- /dev/null
+++ b/usr/src/tools/smatch/src/smatch_data/db/insert_manual_states.pl
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+use File::Basename;
+use DBI;
+
+my $bin_dir = dirname($0);
+my $project = shift;
+my $db_file = shift;
+if (!defined($db_file)) {
+ print "usage: $0 <project> <db_file>\n";
+ exit(1);
+}
+my $insertions = "$bin_dir/$project.insert.return_states";
+
+my $db = DBI->connect("dbi:SQLite:$db_file", "", "", {AutoCommit => 0});
+$db->do("PRAGMA cache_size = 800000");
+$db->do("PRAGMA journal_mode = OFF");
+$db->do("PRAGMA count_changes = OFF");
+$db->do("PRAGMA temp_store = MEMORY");
+$db->do("PRAGMA locking = EXCLUSIVE");
+
+sub insert_record($$$$$$$)
+{
+ my $file = shift;
+ my $func = shift;
+ my $ret = shift;
+ my $type = shift;
+ my $param = shift;
+ my $key = shift;
+ my $value = shift;
+
+# print "file = '$file' func = '$func' ret = $ret\n";
+# print "type = $type param = $param, key = $key, value = '$value'\n";
+# print "select file, return_id, return, static from return_states where function = '$func' and return = '$ret' and type = 0;'\n";
+
+ my $sth;
+ if ($file ne '') {
+ $sth = $db->prepare("select file, return_id, static from return_states where file = ? and function = ? and return = ? and type = 0;");
+ $sth->execute($file, $func, $ret);
+ } else {
+ $sth = $db->prepare("select file, return_id, static from return_states where function = ? and return = ? and type = 0;");
+ $sth->execute($func, $ret);
+ }
+
+ my $insert = $db->prepare("insert into return_states values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
+ while (my @row = $sth->fetchrow_array()) {
+ my $file = $row[0];
+ my $return_id = $row[1];
+ my $static = $row[2];
+
+ $insert->execute($file, $func, 0, $return_id, $ret, $static, $type, $param, $key, $value);
+ }
+}
+
+my ($ret, $insert, $file, $func, $type, $param, $key, $value);
+
+open(FILE, "<$insertions");
+while (<FILE>) {
+
+ if ($_ =~ /^\s*#/) {
+ next;
+ }
+
+ ($ret, $insert) = split(/\|/, $_);
+
+ if ($ret =~ /(.+),\W*(.+),\W*"(.*)"/) {
+ $file = $1;
+ $func = $2;
+ $ret = $3;
+ } elsif ($ret =~ /(.+),\W*"(.*)"/) {
+ $file = "";
+ $func = $1;
+ $ret = $2;
+ } else {
+ next;
+ }
+
+ ($type, $param, $key, $value) = split(/,/, $insert);
+
+ $type = int($type);
+ $param = int($param);
+ $key =~ s/^["\s]+|["\s]+$//g;
+ $value =~ s/^["\s]+|["\s]+$//g;
+ chomp($value);
+
+ insert_record($file, $func, $ret, $type, $param, $key, $value);
+}
+close(FILE);
+
+$db->commit();
+$db->disconnect();
diff --git a/usr/src/tools/smatch/src/smatch_data/db/kernel.insert.return_states b/usr/src/tools/smatch/src/smatch_data/db/kernel.insert.return_states
new file mode 100644
index 0000000000..2b1bb64d6f
--- /dev/null
+++ b/usr/src/tools/smatch/src/smatch_data/db/kernel.insert.return_states
@@ -0,0 +1,12 @@
+# The format to insert is:
+# file (optional), function, "return values" | type, param, "key", "value"
+#
+
+mlx5_cmd_comp_handler, "" | 8021, -2, "*sem", ""
+smc_connect_abort, "1-s32max[==$1]" | 8021, -2, "smc_client_lgr_pending", ""
+smc_connect_abort, "s32min-(-12),(-10)-(-1)[==$1]" | 8021, -2, "smc_client_lgr_pending", ""
+smc_connect_abort, "1-s32max[==$1]" | 8021, -2, "smc_server_lgr_pending", ""
+smc_connect_abort, "s32min-(-12),(-10)-(-1)[==$1]" | 8021, -2, "smc_server_lgr_pending", ""
+dlfb_submit_urb, "0" | 8021, 0, "$->urbs.limit_sem", ""
+hfs_find_exit, "" | 8021, 0, "$->tree->tree_lock", ""
+btrfs_tree_unlock, "" | 8021, 0, "$->lock", ""
diff --git a/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions b/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions
index 029d02aad9..5097699c3a 100644
--- a/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions
+++ b/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions
@@ -12,3 +12,4 @@ nostore_generate_key_pair
sadb_common_add
segvn_fault_vnodepages
tcp_input_data
+zfs_setattr
diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions b/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions
index 388824e15f..94ef91d670 100644
--- a/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions
+++ b/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions
@@ -12,3 +12,5 @@ __write_once_size
atomic_set
atomic_read
notifier_to_errno
+ipmi_ssif_lock_cond
+smc_connect_abort
diff --git a/usr/src/tools/smatch/src/smatch_db.c b/usr/src/tools/smatch/src/smatch_db.c
index 18c5ce5025..e932d315ab 100644
--- a/usr/src/tools/smatch/src/smatch_db.c
+++ b/usr/src/tools/smatch/src/smatch_db.c
@@ -27,8 +27,12 @@ struct sqlite3 *smatch_db;
struct sqlite3 *mem_db;
struct sqlite3 *cache_db;
+int debug_db;
+
static int return_id;
+static void call_return_state_hooks(struct expression *expr);
+
#define SQLITE_CACHE_PAGES 1000
struct def_callback {
@@ -105,7 +109,7 @@ static int print_sql_output(void *unused, int argc, char **argv, char **azColNam
for (i = 0; i < argc; i++) {
if (i != 0)
- printf(", ");
+ sm_printf(", ");
sm_printf("%s", argv[i]);
}
sm_printf("\n");
@@ -120,7 +124,7 @@ void sql_exec(struct sqlite3 *db, int (*callback)(void*, int, char**, char**), v
if (!db)
return;
- if (option_debug) {
+ if (option_debug || debug_db) {
sm_msg("%s", sql);
if (strncasecmp(sql, "select", strlen("select")) == 0)
sqlite3_exec(db, sql, print_sql_output, NULL, NULL);
@@ -801,6 +805,61 @@ int is_recursive_member(const char *name)
}
}
+char *sm_to_arg_name(struct expression *expr, struct sm_state *sm)
+{
+ struct symbol *sym;
+ const char *sm_name;
+ char *name;
+ bool is_address = false;
+ bool add_star = false;
+ char buf[256];
+ char *ret = NULL;
+ int len;
+
+ expr = strip_expr(expr);
+ if (!expr)
+ return NULL;
+
+ if (expr->type == EXPR_PREOP && expr->op == '&') {
+ expr = strip_expr(expr->unop);
+ is_address = true;
+ }
+
+ name = expr_to_var_sym(expr, &sym);
+ if (!name || !sym)
+ goto free;
+ if (sym != sm->sym)
+ goto free;
+
+ sm_name = sm->name;
+ add_star = false;
+ if (sm_name[0] == '*') {
+ add_star = true;
+ sm_name++;
+ }
+
+ len = strlen(name);
+ if (strncmp(name, sm_name, len) != 0)
+ goto free;
+ if (sm_name[len] == '\0') {
+ snprintf(buf, sizeof(buf), "%s%s$",
+ add_star ? "*" : "", is_address ? "*" : "");
+ } else {
+ if (sm_name[len] != '.' && sm_name[len] != '-')
+ goto free;
+ if (sm_name[len] == '-')
+ len++;
+ // FIXME does is_address really imply that sm_name[len] == '-'
+ snprintf(buf, sizeof(buf), "%s$->%s", add_star ? "*" : "",
+ sm_name + len);
+ }
+
+ ret = alloc_sname(buf);
+free:
+ free_string(name);
+ return ret;
+}
+
static void print_struct_members(struct expression *call, struct expression *expr, int param, int offset, struct stree *stree,
void (*callback)(struct expression *call, int param, char *printed_name, struct sm_state *sm))
{
@@ -1417,12 +1476,18 @@ static void match_return_info(int return_id, char *return_ranges, struct express
sql_insert_return_states(return_id, return_ranges, INTERNAL, -1, "", function_signature());
}
-static void call_return_state_hooks_conditional(struct expression *expr)
+static bool call_return_state_hooks_conditional(struct expression *expr)
{
- struct returned_state_callback *cb;
- struct range_list *rl;
- const char *return_ranges;
int final_pass_orig = final_pass;
+ static int recurse;
+
+ if (recurse >= 2)
+ return false;
+ if (!expr ||
+ (expr->type != EXPR_CONDITIONAL && expr->type != EXPR_SELECT))
+ return false;
+
+ recurse++;
__push_fake_cur_stree();
@@ -1430,28 +1495,18 @@ static void call_return_state_hooks_conditional(struct expression *expr)
__split_whole_condition(expr->conditional);
final_pass = final_pass_orig;
- return_ranges = get_return_ranges_str(expr->cond_true ?: expr->conditional, &rl);
-
- set_state(RETURN_ID, "return_ranges", NULL, alloc_estate_rl(rl));
-
- return_id++;
- FOR_EACH_PTR(returned_state_callbacks, cb) {
- cb->callback(return_id, (char *)return_ranges, expr->cond_true);
- } END_FOR_EACH_PTR(cb);
+ call_return_state_hooks(expr->cond_true ?: expr->conditional);
__push_true_states();
__use_false_states();
- return_ranges = get_return_ranges_str(expr->cond_false, &rl);
- set_state(RETURN_ID, "return_ranges", NULL, alloc_estate_rl(rl));
-
- return_id++;
- FOR_EACH_PTR(returned_state_callbacks, cb) {
- cb->callback(return_id, (char *)return_ranges, expr->cond_false);
- } END_FOR_EACH_PTR(cb);
+ call_return_state_hooks(expr->cond_false);
__merge_true_states();
__free_fake_cur_stree();
+
+ recurse--;
+ return true;
}
static void call_return_state_hooks_compare(struct expression *expr)
@@ -1536,8 +1591,12 @@ static int split_possible_helper(struct sm_state *sm, struct expression *expr)
FOR_EACH_PTR(sm->possible, tmp) {
if (tmp->merged)
continue;
+ if (ptr_in_list(tmp, already_handled))
+ continue;
+ add_ptr_list(&already_handled, tmp);
nr_possible++;
} END_FOR_EACH_PTR(tmp);
+ free_slist(&already_handled);
nr_states = get_db_state_count();
if (nr_states * nr_possible >= 2000)
return 0;
@@ -1593,6 +1652,9 @@ static bool has_possible_negative(struct sm_state *sm)
{
struct sm_state *tmp;
+ if (!type_signed(estate_type(sm->state)))
+ return false;
+
FOR_EACH_PTR(sm->possible, tmp) {
if (!estate_rl(tmp->state))
continue;
@@ -1604,7 +1666,7 @@ static bool has_possible_negative(struct sm_state *sm)
return false;
}
-static bool has_possible_zero_null(struct sm_state *sm)
+static bool has_separate_zero_null(struct sm_state *sm)
{
struct sm_state *tmp;
sval_t sval;
@@ -1626,8 +1688,8 @@ static int split_positive_from_negative(struct expression *expr)
struct range_list *rl;
const char *return_ranges;
struct range_list *ret_rl;
+ bool separate_zero;
int undo;
- bool has_zero;
/* We're going to print the states 3 times */
if (get_db_state_count() > 10000 / 3)
@@ -1635,8 +1697,6 @@ static int split_positive_from_negative(struct expression *expr)
if (!get_implied_rl(expr, &rl) || !rl)
return 0;
- if (is_whole_rl(rl) || is_whole_rl_non_zero(rl))
- return 0;
/* Forget about INT_MAX and larger */
if (rl_max(rl).value <= 0)
return 0;
@@ -1648,9 +1708,9 @@ static int split_positive_from_negative(struct expression *expr)
return 0;
if (!has_possible_negative(sm))
return 0;
- has_zero = has_possible_zero_null(sm);
+ separate_zero = has_separate_zero_null(sm);
- if (!assume(compare_expression(expr, has_zero ? '>' : SPECIAL_GTE, zero_expr())))
+ if (!assume(compare_expression(expr, separate_zero ? '>' : SPECIAL_GTE, zero_expr())))
return 0;
return_id++;
@@ -1662,7 +1722,7 @@ static int split_positive_from_negative(struct expression *expr)
end_assume();
- if (has_zero) {
+ if (separate_zero) {
undo = assume(compare_expression(expr, SPECIAL_EQUAL, zero_expr()));
return_id++;
@@ -1719,7 +1779,7 @@ static int call_return_state_hooks_split_null_non_null_zero(struct expression *e
return 0;
if (estate_min(state).value == 0 && estate_max(state).value == 0)
return 0;
- if (!has_possible_zero_null(sm))
+ if (!has_separate_zero_null(sm))
return 0;
nr_states = get_db_state_count();
@@ -1761,6 +1821,37 @@ static int call_return_state_hooks_split_null_non_null_zero(struct expression *e
return 1;
}
+static bool is_kernel_success_fail(struct sm_state *sm)
+{
+ struct sm_state *tmp;
+ struct range_list *rl;
+ bool has_zero = false;
+ bool has_neg = false;
+
+ if (!type_signed(estate_type(sm->state)))
+ return false;
+
+ FOR_EACH_PTR(sm->possible, tmp) {
+ rl = estate_rl(tmp->state);
+ if (!rl)
+ return false;
+ if (rl_min(rl).value == 0 && rl_max(rl).value == 0) {
+ has_zero = true;
+ continue;
+ }
+ has_neg = true;
+ if (rl_min(rl).value >= -4095 && rl_max(rl).value < 0)
+ continue;
+ if (strcmp(tmp->state->name, "s32min-(-1)") == 0)
+ continue;
+ if (strcmp(tmp->state->name, "s32min-(-1),1-s32max") == 0)
+ continue;
+ return false;
+ } END_FOR_EACH_PTR(tmp);
+
+ return has_zero && has_neg;
+}
+
static int call_return_state_hooks_split_success_fail(struct expression *expr)
{
struct sm_state *sm;
@@ -1777,7 +1868,7 @@ static int call_return_state_hooks_split_success_fail(struct expression *expr)
return 0;
nr_states = get_db_state_count();
- if (nr_states > 1500)
+ if (nr_states > 2000)
return 0;
sm = get_sm_state_expr(SMATCH_EXTRA, expr);
@@ -1785,18 +1876,13 @@ static int call_return_state_hooks_split_success_fail(struct expression *expr)
return 0;
if (ptr_list_size((struct ptr_list *)sm->possible) == 1)
return 0;
+ if (!is_kernel_success_fail(sm))
+ return 0;
rl = estate_rl(sm->state);
if (!rl)
return 0;
- if (rl_min(rl).value < -4095 || rl_min(rl).value >= 0)
- return 0;
- if (rl_max(rl).value != 0)
- return 0;
- if (!has_possible_zero_null(sm))
- return 0;
-
__push_fake_cur_stree();
final_pass = 0;
@@ -1842,15 +1928,6 @@ static int is_boolean(struct expression *expr)
return 0;
}
-static int is_conditional(struct expression *expr)
-{
- if (!expr)
- return 0;
- if (expr->type == EXPR_CONDITIONAL || expr->type == EXPR_SELECT)
- return 1;
- return 0;
-}
-
static int splitable_function_call(struct expression *expr)
{
struct sm_state *sm;
@@ -1908,7 +1985,6 @@ static int split_on_bool_sm(struct sm_state *sm, struct expression *expr)
const char *return_ranges;
struct sm_state *tmp;
int ret = 0;
- int nr_possible, nr_states;
struct state_list *already_handled = NULL;
if (!sm || !sm->merged)
@@ -1917,12 +1993,6 @@ static int split_on_bool_sm(struct sm_state *sm, struct expression *expr)
if (too_many_possible(sm))
return 0;
- /* bail if it gets too complicated */
- nr_possible = ptr_list_size((struct ptr_list *)sm->possible);
- nr_states = get_db_state_count();
- if (nr_states * nr_possible >= 2000)
- return 0;
-
FOR_EACH_PTR(sm->possible, tmp) {
if (tmp->merged)
continue;
@@ -1961,6 +2031,10 @@ static int split_by_bool_param(struct expression *expr)
sm = get_sm_state(SMATCH_EXTRA, start_sm->name, start_sm->sym);
if (!sm || estate_get_single_value(sm->state, &sval))
return 0;
+
+ if (get_db_state_count() * 2 >= 2000)
+ return 0;
+
return split_on_bool_sm(sm, expr);
}
@@ -1968,9 +2042,7 @@ static int split_by_null_nonnull_param(struct expression *expr)
{
struct symbol *arg;
struct sm_state *sm;
- sval_t zero = {
- .type = &ulong_ctype,
- };
+ int nr_possible;
/* function must only take one pointer */
if (ptr_list_size((struct ptr_list *)cur_func_sym->ctype.base_type->arguments) != 1)
@@ -1987,7 +2059,11 @@ static int split_by_null_nonnull_param(struct expression *expr)
if (!sm)
return 0;
- if (!rl_has_sval(estate_rl(sm->state), zero))
+ if (!has_separate_zero_null(sm))
+ return 0;
+
+ nr_possible = ptr_list_size((struct ptr_list *)sm->possible);
+ if (get_db_state_count() * nr_possible >= 2000)
return 0;
return split_on_bool_sm(sm, expr);
@@ -2038,8 +2114,7 @@ static void call_return_state_hooks(struct expression *expr)
(is_condition(expr) || is_boolean(expr))) {
call_return_state_hooks_compare(expr);
return;
- } else if (is_conditional(expr)) {
- call_return_state_hooks_conditional(expr);
+ } else if (call_return_state_hooks_conditional(expr)) {
return;
} else if (call_return_state_hooks_split_possible(expr)) {
return;
@@ -2492,7 +2567,7 @@ char *get_variable_from_key(struct expression *arg, const char *key, struct symb
{
char buf[256];
char *tmp;
- bool add_star = false;
+ int star_cnt = 0;
if (!arg)
return NULL;
@@ -2516,25 +2591,30 @@ char *get_variable_from_key(struct expression *arg, const char *key, struct symb
}
}
- if (key[0] == '*') {
- add_star = true;
+ while (key[0] == '*') {
+ star_cnt++;
key++;
}
+ if (arg->type == EXPR_PREOP && arg->op == '&' && star_cnt) {
+ arg = strip_expr(arg->unop);
+ star_cnt--;
+ }
+
if (arg->type == EXPR_PREOP && arg->op == '&') {
arg = strip_expr(arg->unop);
tmp = expr_to_var_sym(arg, sym);
if (!tmp)
return NULL;
- snprintf(buf, sizeof(buf), "%s%s.%s",
- add_star ? "*" : "", tmp, key + 3);
+ snprintf(buf, sizeof(buf), "%.*s%s.%s",
+ star_cnt, "**********", tmp, key + 3);
return alloc_string(buf);
}
tmp = expr_to_var_sym(arg, sym);
if (!tmp)
return NULL;
- snprintf(buf, sizeof(buf), "%s%s%s", add_star ? "*" : "", tmp, key + 1);
+ snprintf(buf, sizeof(buf), "%.*s%s%s", star_cnt, "**********", tmp, key + 1);
free_string(tmp);
return alloc_string(buf);
}
@@ -2550,26 +2630,29 @@ char *get_chunk_from_key(struct expression *arg, char *key, struct symbol **sym,
const char *state_name_to_param_name(const char *state_name, const char *param_name)
{
+ int star_cnt = 0;
int name_len;
- static char buf[256];
- bool add_star = false;
+ char buf[256];
name_len = strlen(param_name);
- if (state_name[0] == '*') {
- add_star = true;
+ while (state_name[0] == '*') {
+ star_cnt++;
state_name++;
}
+ /* ten out of ten stars! */
+ if (star_cnt > 10)
+ return NULL;
+
if (strcmp(state_name, param_name) == 0) {
- snprintf(buf, sizeof(buf), "%s$", add_star ? "*" : "");
+ snprintf(buf, sizeof(buf), "%.*s$", star_cnt, "**********");
return alloc_sname(buf);
}
if (state_name[name_len] == '-' && /* check for '-' from "->" */
strncmp(state_name, param_name, name_len) == 0) {
- snprintf(buf, sizeof(buf), "%s$%s",
- add_star ? "*" : "", state_name + name_len);
+ snprintf(buf, sizeof(buf), "%.*s$%s", star_cnt, "**********", state_name + name_len);
return alloc_sname(buf);
}
return NULL;
diff --git a/usr/src/tools/smatch/src/smatch_expressions.c b/usr/src/tools/smatch/src/smatch_expressions.c
index a65222848a..97f3c98ff6 100644
--- a/usr/src/tools/smatch/src/smatch_expressions.c
+++ b/usr/src/tools/smatch/src/smatch_expressions.c
@@ -85,6 +85,8 @@ struct expression *preop_expression(struct expression *expr, int op)
struct expression *deref_expression(struct expression *expr)
{
+ if (expr->type == EXPR_BINOP)
+ expr = preop_expression(expr, '(');
return preop_expression(expr, '*');
}
@@ -162,6 +164,17 @@ struct expression *string_expression(char *str)
return ret;
}
+struct expression *call_expression(struct expression *fn, struct expression_list *args)
+{
+ struct expression *expr;
+
+ expr = alloc_tmp_expression(fn->pos, EXPR_CALL);
+ expr->fn = fn;
+ expr->args = args;
+
+ return expr;
+}
+
static struct expression *get_expression_from_base_and_str(struct expression *base, const char *addition)
{
struct expression *ret = NULL;
diff --git a/usr/src/tools/smatch/src/smatch_extra.c b/usr/src/tools/smatch/src/smatch_extra.c
index 3dd4aba7f2..9d7744d97c 100644
--- a/usr/src/tools/smatch/src/smatch_extra.c
+++ b/usr/src/tools/smatch/src/smatch_extra.c
@@ -194,6 +194,11 @@ static char *get_pointed_at(const char *name, struct symbol *sym, struct symbol
{
struct expression *assigned;
+ /*
+ * Imagine we have an assignment: "foo = &addr;" then the other name
+ * of "*foo" is addr.
+ */
+
if (name[0] != '*')
return NULL;
if (strcmp(name + 1, sym->ident->name) != 0)
@@ -254,9 +259,10 @@ static char *get_long_name_sym(const char *name, struct symbol *sym, struct symb
/*
* Just prepend the name with a different name/sym and return that.
- * For example, if we set "foo->bar = bar;" then we clamp "bar->baz",
- * that also clamps "foo->bar->baz".
- *
+ * For example, if we set "foo->bar = bar;" then the other name
+ * for "bar->baz" is "foo->bar->baz". Or if we have "foo = bar;" then
+ * the other name for "bar" is "foo". A third option is if we have
+ * "foo = bar;" then another name for "*bar" is "*foo".
*/
FOR_EACH_MY_SM(check_assigned_expr_id, __get_cur_stree(), sm) {
@@ -272,7 +278,17 @@ static char *get_long_name_sym(const char *name, struct symbol *sym, struct symb
found:
if (!use_stack && name[tmp->symbol->ident->len] != '-')
return NULL;
- snprintf(buf, sizeof(buf), "%s%s", sm->name, name + tmp->symbol->ident->len);
+
+ if (name[0] == '*' && strcmp(name + 1, tmp->symbol_name->name) == 0)
+ snprintf(buf, sizeof(buf), "*%s", sm->name);
+ else if (name[tmp->symbol->ident->len] == '-' ||
+ name[tmp->symbol->ident->len] == '.')
+ snprintf(buf, sizeof(buf), "%s%s", sm->name, name + tmp->symbol->ident->len);
+ else if (strcmp(name, tmp->symbol_name->name) == 0)
+ snprintf(buf, sizeof(buf), "%s", sm->name);
+ else
+ return NULL;
+
*new_sym = sm->sym;
return alloc_string(buf);
}
@@ -1086,11 +1102,6 @@ static void match_vanilla_assign(struct expression *left, struct expression *rig
goto free;
}
- if (is_pointer(right) && get_address_rl(right, &rl)) {
- state = alloc_estate_rl(rl);
- goto done;
- }
-
if (get_implied_value(right, &sval)) {
state = alloc_estate_sval(sval_cast(left_type, sval));
goto done;
diff --git a/usr/src/tools/smatch/src/smatch_extra.h b/usr/src/tools/smatch/src/smatch_extra.h
index 40d59baf25..3fb917a5ea 100644
--- a/usr/src/tools/smatch/src/smatch_extra.h
+++ b/usr/src/tools/smatch/src/smatch_extra.h
@@ -214,6 +214,7 @@ struct expression *array_element_expression(struct expression *array, struct exp
struct expression *symbol_expression(struct symbol *sym);
struct expression *string_expression(char *str);
struct expression *compare_expression(struct expression *left, int op, struct expression *right);
+struct expression *call_expression(struct expression *fn, struct expression_list *args);
struct expression *unknown_value_expression(struct expression *expr);
int is_fake_call(struct expression *expr);
struct expression *gen_expression_from_name_sym(const char *name, struct symbol *sym);
diff --git a/usr/src/tools/smatch/src/smatch_function_hooks.c b/usr/src/tools/smatch/src/smatch_function_hooks.c
index 2ff19afb07..d38cff1936 100644
--- a/usr/src/tools/smatch/src/smatch_function_hooks.c
+++ b/usr/src/tools/smatch/src/smatch_function_hooks.c
@@ -1078,6 +1078,7 @@ static int db_return_states_callback(void *_info, int argc, char **argv, char **
value = argv[5];
if (db_info->prev_return_id != -1 && type == INTERNAL) {
+ call_ranged_return_hooks(db_info);
stree = __pop_fake_cur_stree();
if (!db_info->cull)
merge_fake_stree(&db_info->stree, stree);
@@ -1108,9 +1109,20 @@ static int db_return_states_callback(void *_info, int argc, char **argv, char **
ret_range = cast_rl(get_type(db_info->expr), ret_range);
if (type == INTERNAL) {
+ struct smatch_state *state;
+
set_state(-1, "unnull_path", NULL, &true_state);
__add_return_comparison(strip_expr(db_info->expr), ret_str);
__add_return_to_param_mapping(db_info->expr, ret_str);
+ /*
+ * We want to store the return values so that we can split the strees
+ * in smatch_db.c. This uses set_state() directly because it's not a
+ * real smatch_extra state.
+ */
+ snprintf(buf, sizeof(buf), "return %p", db_info->expr);
+ state = alloc_estate_rl(ret_range);
+ set_state(SMATCH_EXTRA, buf, NULL, state);
+ store_return_state(db_info, ret_str, state);
}
FOR_EACH_PTR(db_return_states_list, tmp) {
@@ -1118,13 +1130,6 @@ static int db_return_states_callback(void *_info, int argc, char **argv, char **
tmp->callback(db_info->expr, param, key, value);
} END_FOR_EACH_PTR(tmp);
- /*
- * We want to store the return values so that we can split the strees
- * in smatch_db.c. This uses set_state() directly because it's not a
- * real smatch_extra state.
- */
- snprintf(buf, sizeof(buf), "return %p", db_info->expr);
- set_state(SMATCH_EXTRA, buf, NULL, alloc_estate_rl(ret_range));
return 0;
}
@@ -1148,6 +1153,7 @@ static void db_return_states(struct expression *expr)
__unnullify_path();
sql_select_return_states("return_id, return, type, parameter, key, value",
expr, db_return_states_callback, &db_info);
+ call_ranged_return_hooks(&db_info);
stree = __pop_fake_cur_stree();
if (!db_info.cull)
merge_fake_stree(&db_info.stree, stree);
diff --git a/usr/src/tools/smatch/src/smatch_helper.c b/usr/src/tools/smatch/src/smatch_helper.c
index ed53276516..485f041123 100644
--- a/usr/src/tools/smatch/src/smatch_helper.c
+++ b/usr/src/tools/smatch/src/smatch_helper.c
@@ -299,6 +299,17 @@ static void __get_variable_from_expr(struct symbol **sym_ptr, char *buf,
append(buf, tmp, len);
return;
}
+ case EXPR_FVALUE: {
+ sval_t sval = {};
+ char tmp[25];
+
+ *complicated = 1;
+ if (!get_value(expr, &sval))
+ return;
+ snprintf(tmp, 25, "%s", sval_to_numstr(sval));
+ append(buf, tmp, len);
+ return;
+ }
case EXPR_STRING:
append(buf, "\"", len);
if (expr->string)
@@ -1066,6 +1077,22 @@ int get_param_num(struct expression *expr)
return get_param_num_from_sym(sym);
}
+struct symbol *get_param_sym_from_num(int num)
+{
+ struct symbol *sym;
+ int i;
+
+ if (!cur_func_sym)
+ return NULL;
+
+ i = 0;
+ FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, sym) {
+ if (i++ == num)
+ return sym;
+ } END_FOR_EACH_PTR(sym);
+ return NULL;
+}
+
int ms_since(struct timeval *start)
{
struct timeval end;
diff --git a/usr/src/tools/smatch/src/smatch_implied.c b/usr/src/tools/smatch/src/smatch_implied.c
index b13a7cc049..b498c8e71f 100644
--- a/usr/src/tools/smatch/src/smatch_implied.c
+++ b/usr/src/tools/smatch/src/smatch_implied.c
@@ -142,10 +142,13 @@ static int create_fake_history(struct sm_state *sm, int comparison, struct range
return 0;
if (rl_intersection(true_rl, false_rl)) {
- sm_perror("parsing (%s (%s) %s %s)",
- sm->name, sm->state->name, show_special(comparison), show_rl(rl));
- sm_msg("true_rl = %s false_rl = %s intersection = %s",
- show_rl(true_rl), show_rl(false_rl), show_rl(rl_intersection(true_rl, false_rl)));
+ /*
+ * Normally these won't intersect, but one exception is when
+ * we're dealing with mtags. We have a long list of mtags and
+ * then negate the list. Now it's over the limit for mtag list
+ * length and we squash it down to 4096-ptr_max. So then it's
+ * possible for the true and false rl to overlap.
+ */
return 0;
}
@@ -807,6 +810,31 @@ static int handled_by_extra_states(struct expression *expr,
return handle_zero_comparison(expr, implied_true, implied_false);
}
+static int handled_by_parsed_conditions(struct expression *expr,
+ struct stree **implied_true,
+ struct stree **implied_false)
+{
+ struct state_list *true_stack = NULL;
+ struct state_list *false_stack = NULL;
+ struct stree *pre_stree;
+ struct sm_state *sm;
+
+ sm = parsed_condition_implication_hook(expr, &true_stack, &false_stack);
+ if (!sm)
+ return 0;
+
+ pre_stree = clone_stree(__get_cur_stree());
+
+ *implied_true = filter_stack(sm, pre_stree, false_stack, true_stack);
+ *implied_false = filter_stack(sm, pre_stree, true_stack, false_stack);
+
+ free_stree(&pre_stree);
+ free_slist(&true_stack);
+ free_slist(&false_stack);
+
+ return 1;
+}
+
static int handled_by_stored_conditions(struct expression *expr,
struct stree **implied_true,
struct stree **implied_false)
@@ -837,14 +865,15 @@ static struct stree *saved_implied_false;
static struct stree *extra_saved_implied_true;
static struct stree *extra_saved_implied_false;
-static void separate_extra_states(struct stree **implied_true,
- struct stree **implied_false)
+static void separate_implication_states(struct stree **implied_true,
+ struct stree **implied_false,
+ int owner)
{
struct sm_state *sm;
- /* We process extra states later to preserve the implications. */
+ /* We process these states later to preserve the implications. */
FOR_EACH_SM(*implied_true, sm) {
- if (sm->owner == SMATCH_EXTRA)
+ if (sm->owner == owner)
overwrite_sm_state_stree(&extra_saved_implied_true, sm);
} END_FOR_EACH_SM(sm);
FOR_EACH_SM(extra_saved_implied_true, sm) {
@@ -852,7 +881,7 @@ static void separate_extra_states(struct stree **implied_true,
} END_FOR_EACH_SM(sm);
FOR_EACH_SM(*implied_false, sm) {
- if (sm->owner == SMATCH_EXTRA)
+ if (sm->owner == owner)
overwrite_sm_state_stree(&extra_saved_implied_false, sm);
} END_FOR_EACH_SM(sm);
FOR_EACH_SM(extra_saved_implied_false, sm) {
@@ -864,11 +893,16 @@ static void get_tf_states(struct expression *expr,
struct stree **implied_true,
struct stree **implied_false)
{
- if (handled_by_comparison_hook(expr, implied_true, implied_false))
+ if (handled_by_parsed_conditions(expr, implied_true, implied_false))
return;
+ if (handled_by_comparison_hook(expr, implied_true, implied_false)) {
+ separate_implication_states(implied_true, implied_false, comparison_id);
+ return;
+ }
+
if (handled_by_extra_states(expr, implied_true, implied_false)) {
- separate_extra_states(implied_true, implied_false);
+ separate_implication_states(implied_true, implied_false, SMATCH_EXTRA);
return;
}
@@ -1138,8 +1172,8 @@ void register_implications(int id)
add_hook(&save_implications_hook, CONDITION_HOOK);
add_hook(&set_implied_states, CONDITION_HOOK);
add_hook(&__extra_match_condition, CONDITION_HOOK);
- add_hook(&set_extra_implied_states, CONDITION_HOOK);
add_hook(&__comparison_match_condition, CONDITION_HOOK);
+ add_hook(&set_extra_implied_states, CONDITION_HOOK);
add_hook(&__stored_condition, CONDITION_HOOK);
add_hook(&match_end_func, END_FUNC_HOOK);
}
diff --git a/usr/src/tools/smatch/src/smatch_math.c b/usr/src/tools/smatch/src/smatch_math.c
index 7ef474d680..68ca445619 100644
--- a/usr/src/tools/smatch/src/smatch_math.c
+++ b/usr/src/tools/smatch/src/smatch_math.c
@@ -1398,6 +1398,9 @@ static bool get_rl_sval(struct expression *expr, int implied, int *recurse_cnt,
case EXPR_VALUE:
sval = sval_from_val(expr, expr->value);
break;
+ case EXPR_FVALUE:
+ sval = sval_from_fval(expr, expr->fvalue);
+ break;
case EXPR_PREOP:
handle_preop_rl(expr, implied, recurse_cnt, &rl, &sval);
break;
diff --git a/usr/src/tools/smatch/src/smatch_parsed_conditions.c b/usr/src/tools/smatch/src/smatch_parsed_conditions.c
new file mode 100644
index 0000000000..338d2a2605
--- /dev/null
+++ b/usr/src/tools/smatch/src/smatch_parsed_conditions.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 Oracle.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
+ */
+
+/*
+ * The problem here is something like this:
+ *
+ * return (blah() || whatever()) ? NULL : some_function();
+ *
+ * When we are parsing this what happens is that we first parse all the
+ * expressions "(blah() || whatever()) ? NULL : some_function();" and then
+ * we parse the return statement.
+ *
+ * When we parse the return statement, we say "Oh, this is a conditional. Let's
+ * get all the implications for true and false." But because
+ * "(blah() || whatever())" is a function pointer, that means there aren't any
+ * implications.
+ *
+ * So what this module does is it ties the implications to the expression
+ * pointer so that we can retreive them easily. It's similar to Smatch stored
+ * implications but it doesn't save condition, it saves the pointer.
+ *
+ * We ignore pre loop conditions which Smatch parses twice.
+ *
+ */
+
+#include "smatch.h"
+#include "smatch_slist.h"
+
+static int my_id;
+
+STATE(true_path);
+STATE(false_path);
+
+void record_condition(struct expression *expr)
+{
+ char name[32];
+ sval_t val;
+
+ if (get_value(expr, &val))
+ return;
+
+ if (__in_pre_condition)
+ return;
+
+ snprintf(name, sizeof(name), "condition %p", expr);
+ set_true_false_states(my_id, name, NULL, &true_path, &false_path);
+}
+
+void register_parsed_conditions(int id)
+{
+ my_id = id;
+ add_hook(&record_condition, CONDITION_HOOK);
+}
+
+static void filter_by_sm(struct sm_state *sm,
+ struct state_list **true_stack,
+ struct state_list **false_stack)
+{
+ if (!sm)
+ return;
+
+ if (sm->state == &true_path)
+ add_ptr_list(true_stack, sm);
+ else if (sm->state == &false_path)
+ add_ptr_list(false_stack, sm);
+
+ if (sm->merged) {
+ filter_by_sm(sm->left, true_stack, false_stack);
+ filter_by_sm(sm->right, true_stack, false_stack);
+ }
+}
+
+struct sm_state *parsed_condition_implication_hook(struct expression *expr,
+ struct state_list **true_stack,
+ struct state_list **false_stack)
+{
+ struct state_list *tmp_true = NULL;
+ struct state_list *tmp_false = NULL;
+ struct sm_state *sm, *tmp;
+ char name[32];
+
+ snprintf(name, sizeof(name), "condition %p", expr);
+
+ sm = get_sm_state(my_id, name, NULL);
+ if (!sm)
+ return NULL;
+ if (!sm->merged)
+ return NULL;
+
+ filter_by_sm(sm, &tmp_true, &tmp_false);
+ if (!tmp_true && !tmp_false)
+ return NULL;
+
+ FOR_EACH_PTR(tmp_true, tmp) {
+ add_ptr_list(true_stack, tmp);
+ } END_FOR_EACH_PTR(tmp);
+
+ FOR_EACH_PTR(tmp_false, tmp) {
+ add_ptr_list(false_stack, tmp);
+ } END_FOR_EACH_PTR(tmp);
+
+ free_slist(&tmp_true);
+ free_slist(&tmp_false);
+
+ return sm;
+}
+
diff --git a/usr/src/tools/smatch/src/smatch_ranges.c b/usr/src/tools/smatch/src/smatch_ranges.c
index b30b98b7ff..95fe52bf5f 100644
--- a/usr/src/tools/smatch/src/smatch_ranges.c
+++ b/usr/src/tools/smatch/src/smatch_ranges.c
@@ -134,6 +134,9 @@ static int truncates_nicely(struct symbol *type, sval_t min, sval_t max)
unsigned long long mask;
int bits = type_bits(type);
+ if (type_is_fp(min.type) && !type_is_fp(type))
+ return 0;
+
if (bits >= type_bits(min.type))
return 0;
@@ -432,6 +435,13 @@ static sval_t parse_val(int use_max, struct expression *call, struct symbol *typ
const char *start = c;
sval_t ret;
+ if (type == &float_ctype)
+ return sval_type_fval(type, strtof(start, (char **)endp));
+ else if (type == &double_ctype)
+ return sval_type_fval(type, strtod(start, (char **)endp));
+ else if (type == &ldouble_ctype)
+ return sval_type_fval(type, strtold(start, (char **)endp));
+
if (!strncmp(start, "max", 3)) {
ret = sval_type_max(type);
c += 3;
@@ -564,7 +574,7 @@ static void str_to_rl_helper(struct expression *call, struct symbol *type, const
break;
}
if (*c != '-') {
- sm_msg("debug XXX: trouble parsing %s c = %s", str, c);
+ sm_debug("XXX: trouble parsing %s c = %s", str, c);
break;
}
c++;
diff --git a/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh b/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh
index c8f98d9bdb..938124315f 100755
--- a/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh
+++ b/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh
@@ -59,7 +59,7 @@ if [[ ! -z $CROSS_COMPILE ]] ; then
KERNEL_CROSS_COMPILE="CROSS_COMPILE=$CROSS_COMPILE"
fi
-make $KERNEL_ARCH $KERNEL_CROSS_COMPILE -j${NR_CPU} CHECK="$BIN_DIR/smatch --call-tree --info --param-mapper --spammy --file-output" $TARGET
+make $KERNEL_ARCH $KERNEL_CROSS_COMPILE -j${NR_CPU} CHECK="$BIN_DIR/smatch --call-tree --info --spammy --file-output" $TARGET
find -name \*.c.smatch -exec cat \{\} \; -exec rm \{\} \; > smatch_warns.txt
diff --git a/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh b/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh
index 9f470738e6..1dd9616688 100755
--- a/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh
+++ b/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh
@@ -36,7 +36,7 @@ if [ ! -e smatch_db.sqlite ] ; then
fi
BUILD_STATUS=0
-$SCRIPT_DIR/test_kernel.sh --call-tree --info --param-mapper --spammy --data=$DATA_DIR || BUILD_STATUS=$?
+$SCRIPT_DIR/test_kernel.sh --call-tree --info --spammy --data=$DATA_DIR || BUILD_STATUS=$?
for i in $SCRIPT_DIR/gen_* ; do
$i smatch_warns.txt -p=kernel
diff --git a/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh b/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh
index e1dc44f271..cdc1c7a569 100755
--- a/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh
+++ b/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh
@@ -25,6 +25,7 @@ ${bin_dir}/trace_params.pl $file rds_info_copy 1 >> $tmp
${bin_dir}/trace_params.pl $file nla_put 3 >> $tmp
${bin_dir}/trace_params.pl $file snd_timer_user_append_to_tqueue 1 >> $tmp
${bin_dir}/trace_params.pl $file __send_signal 1 >> $tmp
+${bin_dir}/trace_params.pl $file usb_bulk_msg 2 >> $tmp
cat $tmp | sort -u > $tmp2
mv $tmp2 $tmp
diff --git a/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh b/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh
index 42b2f31bb9..0cb8a6e2d8 100755
--- a/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh
+++ b/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh
@@ -99,8 +99,10 @@ mv $MSG_FILE.1 $MSG_FILE
git commit $AMEND -F $MSG_FILE
-to_addr=$(./scripts/get_maintainer.pl -f --noroles --norolestats $fullname | head -n 1)
-cc_addr=$(./scripts/get_maintainer.pl -f --noroles --norolestats $fullname | tail -n +2 | \
+git format-patch HEAD^ --stdout >> $MSG_FILE
+
+to_addr=$(./scripts/get_maintainer.pl --noroles --norolestats $MSG_FILE | head -n 1)
+cc_addr=$(./scripts/get_maintainer.pl --noroles --norolestats $MSG_FILE | tail -n +2 | \
perl -ne 's/\n$/, /; print')
cc_addr="$cc_addr, kernel-janitors@vger.kernel.org"
@@ -118,3 +120,4 @@ echo "Press ENTER to continue"
read unused
mutt -H $MAIL_FILE
+rm -f $MSG_FILE
diff --git a/usr/src/tools/smatch/src/smatch_states.c b/usr/src/tools/smatch/src/smatch_states.c
index 013405f638..7ae8f4a02b 100644
--- a/usr/src/tools/smatch/src/smatch_states.c
+++ b/usr/src/tools/smatch/src/smatch_states.c
@@ -1095,6 +1095,15 @@ void __merge_switches(struct expression *switch_expr, struct range_list *case_rl
struct stree *implied_stree;
stree = pop_stree(&switch_stack);
+ if (!stree) {
+ /*
+ * If the cur_stree was NULL before the start of the switch
+ * statement then we don't want to unnullify it.
+ *
+ */
+ push_stree(&switch_stack, stree);
+ return;
+ }
implied_stree = __implied_case_stree(switch_expr, case_rl, &remaining_cases, &stree);
merge_stree(&cur_stree, implied_stree);
free_stree(&implied_stree);
diff --git a/usr/src/tools/smatch/src/smatch_sval.c b/usr/src/tools/smatch/src/smatch_sval.c
index 6fe29cdab7..c103550bba 100644
--- a/usr/src/tools/smatch/src/smatch_sval.c
+++ b/usr/src/tools/smatch/src/smatch_sval.c
@@ -74,6 +74,15 @@ sval_t sval_type_val(struct symbol *type, long long val)
return ret;
}
+sval_t sval_type_fval(struct symbol *type, long double fval)
+{
+ sval_t ret;
+
+ ret.type = &ldouble_ctype;
+ ret.ldvalue = fval;
+ return sval_cast(type, ret);
+}
+
sval_t sval_from_val(struct expression *expr, long long val)
{
sval_t ret;
@@ -85,6 +94,17 @@ sval_t sval_from_val(struct expression *expr, long long val)
return ret;
}
+sval_t sval_from_fval(struct expression *expr, long double fval)
+{
+ sval_t ret;
+
+ ret.type = &ldouble_ctype;
+ ret.ldvalue = fval;
+ ret = sval_cast(get_type(expr), ret);
+
+ return ret;
+}
+
int sval_is_ptr(sval_t sval)
{
if (!sval.type)
@@ -92,6 +112,11 @@ int sval_is_ptr(sval_t sval)
return (sval.type->type == SYM_PTR || sval.type->type == SYM_ARRAY);
}
+bool sval_is_fp(sval_t sval)
+{
+ return type_is_fp(sval.type);
+}
+
int sval_unsigned(sval_t sval)
{
if (is_ptr_type(sval.type))
@@ -134,10 +159,25 @@ int sval_is_positive(sval_t sval)
return !sval_is_negative(sval);
}
+static bool fp_is_min(sval_t sval)
+{
+ if (sval.type == &float_ctype)
+ return sval.fvalue == -FLT_MAX;
+ if (sval.type == &double_ctype)
+ return sval.dvalue == -DBL_MAX;
+ if (sval.type == &ldouble_ctype)
+ return sval.ldvalue == -LDBL_MAX;
+ sm_perror("%s: bad type: '%s'", __func__, type_to_str(sval.type));
+ return false;
+}
+
int sval_is_min(sval_t sval)
{
sval_t min = sval_type_min(sval.type);
+ if (sval_is_fp(sval))
+ return fp_is_min(sval);
+
if (sval_unsigned(sval)) {
if (sval.uvalue == 0)
return 1;
@@ -147,10 +187,25 @@ int sval_is_min(sval_t sval)
return (sval.value <= min.value);
}
+static bool fp_is_max(sval_t sval)
+{
+ if (sval.type == &float_ctype)
+ return sval.fvalue == FLT_MAX;
+ if (sval.type == &double_ctype)
+ return sval.dvalue == DBL_MAX;
+ if (sval.type == &ldouble_ctype)
+ return sval.ldvalue == LDBL_MAX;
+ sm_perror("%s: bad type: '%s'", __func__, type_to_str(sval.type));
+ return false;
+}
+
int sval_is_max(sval_t sval)
{
sval_t max = sval_type_max(sval.type);
+ if (sval_is_fp(sval))
+ return fp_is_max(sval);
+
if (sval_unsigned(sval))
return (sval.uvalue >= max.value);
return (sval.value >= max.value);
@@ -160,6 +215,10 @@ int sval_is_a_min(sval_t sval)
{
if (sval_is_min(sval))
return 1;
+
+ if (sval_is_fp(sval))
+ return 0;
+
if (sval_signed(sval) && sval.value == SHRT_MIN)
return 1;
if (sval_signed(sval) && sval.value == INT_MIN)
@@ -173,6 +232,10 @@ int sval_is_a_max(sval_t sval)
{
if (sval_is_max(sval))
return 1;
+
+ if (sval_is_fp(sval))
+ return 0;
+
if (sval.uvalue == SHRT_MAX)
return 1;
if (sval.uvalue == INT_MAX)
@@ -193,6 +256,9 @@ int sval_is_a_max(sval_t sval)
int sval_is_negative_min(sval_t sval)
{
+ if (sval_is_fp(sval))
+ return 0;
+
if (!sval_is_negative(sval))
return 0;
return sval_is_min(sval);
@@ -254,6 +320,13 @@ int sval_too_high(struct symbol *type, sval_t sval)
int sval_fits(struct symbol *type, sval_t sval)
{
+ /* everything fits into floating point */
+ if (type_is_fp(type))
+ return 1;
+ /* floating points don't fit into int */
+ if (type_is_fp(sval.type))
+ return 0;
+
if (sval_too_low(type, sval))
return 0;
if (sval_too_high(type, sval))
@@ -261,6 +334,71 @@ int sval_fits(struct symbol *type, sval_t sval)
return 1;
}
+static sval_t cast_to_fp(struct symbol *type, sval_t sval)
+{
+ sval_t ret = {};
+
+ ret.type = type;
+ if (type == &float_ctype) {
+ if (!sval_is_fp(sval)) {
+ if (sval_unsigned(sval))
+ ret.fvalue = sval.uvalue;
+ else
+ ret.fvalue = sval.value;
+ } else if (sval.type == &float_ctype)
+ ret.fvalue = sval.fvalue;
+ else if (sval.type == &double_ctype)
+ ret.fvalue = sval.dvalue;
+ else
+ ret.fvalue = sval.ldvalue;
+ } else if (type == &double_ctype) {
+ if (!sval_is_fp(sval)) {
+ if (sval_unsigned(sval))
+ ret.dvalue = sval.uvalue;
+ else
+ ret.dvalue = sval.value;
+ } else if (sval.type == &float_ctype)
+ ret.dvalue = sval.fvalue;
+ else if (sval.type == &double_ctype)
+ ret.dvalue = sval.dvalue;
+ else
+ ret.dvalue = sval.ldvalue;
+ } else if (type == &ldouble_ctype) {
+ if (!sval_is_fp(sval)) {
+ if (sval_unsigned(sval))
+ ret.ldvalue = (long double)sval.uvalue;
+ else
+ ret.ldvalue = (long double)sval.value;
+ } else if (sval.type == &float_ctype)
+ ret.ldvalue = sval.fvalue;
+ else if (sval.type == &double_ctype)
+ ret.ldvalue = sval.dvalue;
+ else
+ ret.ldvalue = sval.ldvalue;
+ } else {
+ sm_perror("%s: bad type: %s", __func__, type_to_str(type));
+ }
+
+ return ret;
+}
+
+static sval_t cast_from_fp(struct symbol *type, sval_t sval)
+{
+ sval_t ret = {};
+
+ ret.type = &llong_ctype;
+ if (sval.type == &float_ctype)
+ ret.value = sval.fvalue;
+ else if (sval.type == &double_ctype)
+ ret.value = sval.dvalue;
+ else if (sval.type == &ldouble_ctype)
+ ret.value = sval.ldvalue;
+ else
+ sm_perror("%s: bad type: %s", __func__, type_to_str(type));
+
+ return sval_cast(type, ret);
+}
+
sval_t sval_cast(struct symbol *type, sval_t sval)
{
sval_t ret;
@@ -268,6 +406,11 @@ sval_t sval_cast(struct symbol *type, sval_t sval)
if (!type)
type = &int_ctype;
+ if (type_is_fp(type))
+ return cast_to_fp(type, sval);
+ if (type_is_fp(sval.type))
+ return cast_from_fp(type, sval);
+
ret.type = type;
switch (sval_bits(ret)) {
case 1:
@@ -626,9 +769,28 @@ unsigned long long sval_fls_mask(sval_t sval)
return fls_mask(sval.uvalue);
}
+static char *fp_to_str(sval_t sval)
+{
+ char buf[32];
+
+ if (sval.type == &float_ctype)
+ snprintf(buf, sizeof(buf), "%f", sval.fvalue);
+ else if (sval.type == &double_ctype)
+ snprintf(buf, sizeof(buf), "%e", sval.dvalue);
+ else if (sval.type == &ldouble_ctype) {
+ snprintf(buf, sizeof(buf), "%Lf", sval.ldvalue);
+ } else
+ snprintf(buf, sizeof(buf), "nan");
+
+ return alloc_sname(buf);
+}
+
const char *sval_to_str(sval_t sval)
{
- char buf[30];
+ char buf[32];
+
+ if (sval_is_fp(sval))
+ return fp_to_str(sval);
if (sval_is_ptr(sval) && sval.value == valid_ptr_max)
return "ptr_max";
@@ -671,7 +833,7 @@ const char *sval_to_str_or_err_ptr(sval_t sval)
!is_ptr_type(sval.type))
return sval_to_str(sval);
- if (sval.uvalue >= -4905ULL) {
+ if (!sval_is_fp(sval) && sval.uvalue >= -4905ULL) {
snprintf(buf, sizeof(buf), "(%lld)", sval.value);
return alloc_sname(buf);
}
@@ -683,6 +845,9 @@ const char *sval_to_numstr(sval_t sval)
{
char buf[30];
+ if (type_is_fp(sval.type))
+ return fp_to_str(sval);
+
if (sval_unsigned(sval))
snprintf(buf, sizeof(buf), "%llu", sval.value);
else if (sval.value < 0)
diff --git a/usr/src/tools/smatch/src/smatch_type.c b/usr/src/tools/smatch/src/smatch_type.c
index 305a0b5c33..7e2e29ade5 100644
--- a/usr/src/tools/smatch/src/smatch_type.c
+++ b/usr/src/tools/smatch/src/smatch_type.c
@@ -77,6 +77,24 @@ static struct symbol *get_binop_type(struct expression *expr)
if (!right)
return NULL;
+ if (type_is_fp(left)) {
+ if (type_is_fp(right)) {
+ if (type_bits(left) > type_bits(right))
+ return left;
+ return right;
+ }
+ return left;
+ }
+
+ if (type_is_fp(right)) {
+ if (type_is_fp(left)) {
+ if (type_bits(right) > type_bits(left))
+ return right;
+ return left;
+ }
+ return right;
+ }
+
if (expr->op == '-' &&
(is_ptr_type(left) && is_ptr_type(right)))
return ssize_t_ctype;
@@ -143,10 +161,30 @@ static struct symbol *get_symbol_from_deref(struct expression *expr)
return get_real_base_type(sym);
}
+static struct symbol *handle__builtin_choose_expr(struct expression *expr)
+{
+ struct expression *const_expr, *expr1, *expr2;
+ sval_t sval;
+
+ const_expr = get_argument_from_call_expr(expr->args, 0);
+ expr1 = get_argument_from_call_expr(expr->args, 1);
+ expr2 = get_argument_from_call_expr(expr->args, 2);
+
+ if (!get_value(const_expr, &sval) || !expr1 || !expr2)
+ return NULL;
+ if (sval.value)
+ return get_type(expr1);
+ else
+ return get_type(expr2);
+}
+
static struct symbol *get_return_type(struct expression *expr)
{
struct symbol *tmp;
+ if (sym_name_is("__builtin_choose_expr", expr->fn))
+ return handle__builtin_choose_expr(expr);
+
tmp = get_type(expr->fn);
if (!tmp)
return NULL;
@@ -403,10 +441,27 @@ int returns_pointer(struct symbol *sym)
return 0;
}
+static sval_t fp_max(struct symbol *type)
+{
+ sval_t ret = { .type = type };
+
+ if (type == &float_ctype)
+ ret.fvalue = FLT_MAX;
+ else if (type == &double_ctype)
+ ret.dvalue = DBL_MAX;
+ else
+ ret.ldvalue = LDBL_MAX;
+
+ return ret;
+}
+
sval_t sval_type_max(struct symbol *base_type)
{
sval_t ret;
+ if (type_is_fp(base_type))
+ return fp_max(base_type);
+
if (!base_type || !type_bits(base_type))
base_type = &llong_ctype;
ret.type = base_type;
@@ -415,10 +470,27 @@ sval_t sval_type_max(struct symbol *base_type)
return ret;
}
+static sval_t fp_min(struct symbol *type)
+{
+ sval_t ret = { .type = type };
+
+ if (type == &float_ctype)
+ ret.fvalue = -FLT_MAX;
+ else if (type == &double_ctype)
+ ret.dvalue = -DBL_MAX;
+ else
+ ret.ldvalue = -LDBL_MAX;
+
+ return ret;
+}
+
sval_t sval_type_min(struct symbol *base_type)
{
sval_t ret;
+ if (type_is_fp(base_type))
+ return fp_min(base_type);
+
if (!base_type || !type_bits(base_type))
base_type = &llong_ctype;
ret.type = base_type;
diff --git a/usr/src/tools/smatch/src/validation/sm_float1.c b/usr/src/tools/smatch/src/validation/sm_float1.c
new file mode 100644
index 0000000000..2701767443
--- /dev/null
+++ b/usr/src/tools/smatch/src/validation/sm_float1.c
@@ -0,0 +1,20 @@
+#include "check_debug.h"
+
+float frob(float x)
+{
+ return x;
+}
+
+int main(int argc, char *argv[])
+{
+ __smatch_implied((long long)frob(3.14));
+}
+
+/*
+ * check-name: smatch floating point #1
+ * check-command: smatch -I.. sm_float1.c
+ *
+ * check-output-start
+sm_float1.c:10 main() implied: frob(3.140000e+00) = '3'
+ * check-output-end
+ */
diff --git a/usr/src/ucblib/libcurses/i386/Makefile b/usr/src/ucblib/libcurses/i386/Makefile
index 75f501557f..aa983b2923 100644
--- a/usr/src/ucblib/libcurses/i386/Makefile
+++ b/usr/src/ucblib/libcurses/i386/Makefile
@@ -26,7 +26,7 @@
include ../Makefile.com
DYNFLAGS += $(DYNFLAGS32)
-BUILD.SO= $(CC) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) -L $(ROOT)/usr/ucblib $(LDLIBS)
+BUILD.SO= $(CC) $(CFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) -L $(ROOT)/usr/ucblib $(LDLIBS)
.KEEP_STATE:
diff --git a/usr/src/ucblib/libdbm/i386/Makefile b/usr/src/ucblib/libdbm/i386/Makefile
index 85705049e2..2d7d1fdc53 100644
--- a/usr/src/ucblib/libdbm/i386/Makefile
+++ b/usr/src/ucblib/libdbm/i386/Makefile
@@ -25,7 +25,7 @@
include ../Makefile.com
-BUILD.SO= $(CC) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) -L $(ROOT)/usr/ucblib $(LDLIBS)
+BUILD.SO= $(CC) $(CFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) -L $(ROOT)/usr/ucblib $(LDLIBS)
.KEEP_STATE:
diff --git a/usr/src/ucblib/librpcsoc/i386/Makefile b/usr/src/ucblib/librpcsoc/i386/Makefile
index 8c2e44b15a..adaefd83fb 100644
--- a/usr/src/ucblib/librpcsoc/i386/Makefile
+++ b/usr/src/ucblib/librpcsoc/i386/Makefile
@@ -26,7 +26,7 @@
include ../Makefile.com
DYNFLAGS += $(DYNFLAGS32)
-BUILD.SO= $(CC) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) -L $(ROOT)/usr/ucblib $(LDLIBS)
+BUILD.SO= $(CC) $(CFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) -L $(ROOT)/usr/ucblib $(LDLIBS)
LIBS = $(DYNLIB)
diff --git a/usr/src/ucblib/libtermcap/i386/Makefile b/usr/src/ucblib/libtermcap/i386/Makefile
index 85705049e2..2d7d1fdc53 100644
--- a/usr/src/ucblib/libtermcap/i386/Makefile
+++ b/usr/src/ucblib/libtermcap/i386/Makefile
@@ -25,7 +25,7 @@
include ../Makefile.com
-BUILD.SO= $(CC) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) -L $(ROOT)/usr/ucblib $(LDLIBS)
+BUILD.SO= $(CC) $(CFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) -L $(ROOT)/usr/ucblib $(LDLIBS)
.KEEP_STATE:
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 08004c0e87..59eb175515 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1817,6 +1817,8 @@ USB_IA_OBJS += usb_ia.o
SCSA2USB_OBJS += scsa2usb.o usb_ms_bulkonly.o usb_ms_cbi.o
+CCID_OBJS += ccid.o atr.o
+
IPF_OBJS += cfw.o ip_fil_solaris.o fil.o solaris.o ip_state.o ip_frag.o \
ip_nat.o ip_proxy.o ip_auth.o ip_pool.o ip_htable.o ip_lookup.o \
ip_log.o misc.o ip_compat.o ip_nat6.o drand48.o
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index e199892bbd..1d052bdcc2 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -1207,6 +1207,14 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/clients/audio/usb_ah/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/clients/ccid/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(COMMONBASE)/ccid/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/clients/usbskel/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_link.c b/usr/src/uts/common/brand/lx/syscall/lx_link.c
index 4ebf491d23..c4e8fbc93c 100644
--- a/usr/src/uts/common/brand/lx/syscall/lx_link.c
+++ b/usr/src/uts/common/brand/lx/syscall/lx_link.c
@@ -116,6 +116,7 @@ lx_isdir(int atfd, char *path)
if (cstatat_getvp(atfd, path, NO_FOLLOW, &vp, &cr) != 0)
return (B_FALSE);
+ crfree(cr);
is_dir = (vp->v_type == VDIR);
VN_RELE(vp);
diff --git a/usr/src/uts/common/fs/zfs/btree.c b/usr/src/uts/common/fs/zfs/btree.c
index 72c68fb0f5..0c1013d5b7 100644
--- a/usr/src/uts/common/fs/zfs/btree.c
+++ b/usr/src/uts/common/fs/zfs/btree.c
@@ -980,7 +980,7 @@ zfs_btree_bulk_finish(zfs_btree_t *tree)
* Insert value into tree at the location specified by where.
*/
void
-zfs_btree_insert(zfs_btree_t *tree, const void *value,
+zfs_btree_add_idx(zfs_btree_t *tree, const void *value,
const zfs_btree_index_t *where)
{
zfs_btree_index_t idx = {0};
@@ -1294,7 +1294,7 @@ zfs_btree_add(zfs_btree_t *tree, const void *node)
{
zfs_btree_index_t where = {0};
VERIFY3P(zfs_btree_find(tree, node, &where), ==, NULL);
- zfs_btree_insert(tree, node, &where);
+ zfs_btree_add_idx(tree, node, &where);
}
/* Helper function to free a tree node. */
@@ -1542,7 +1542,7 @@ zfs_btree_remove_from_node(zfs_btree_t *tree, zfs_btree_core_t *node,
/* Remove the element at the specific location. */
void
-zfs_btree_remove_from(zfs_btree_t *tree, zfs_btree_index_t *where)
+zfs_btree_remove_idx(zfs_btree_t *tree, zfs_btree_index_t *where)
{
size_t size = tree->bt_elem_size;
zfs_btree_hdr_t *hdr = where->bti_node;
@@ -1778,7 +1778,7 @@ zfs_btree_remove(zfs_btree_t *tree, const void *value)
{
zfs_btree_index_t where = {0};
VERIFY3P(zfs_btree_find(tree, value, &where), !=, NULL);
- zfs_btree_remove_from(tree, &where);
+ zfs_btree_remove_idx(tree, &where);
}
/* Return the number of elements in the tree. */
diff --git a/usr/src/uts/common/fs/zfs/dsl_scan.c b/usr/src/uts/common/fs/zfs/dsl_scan.c
index 81c7d599a4..b619719ba9 100644
--- a/usr/src/uts/common/fs/zfs/dsl_scan.c
+++ b/usr/src/uts/common/fs/zfs/dsl_scan.c
@@ -2848,13 +2848,23 @@ scan_io_queue_fetch_ext(dsl_scan_io_queue_t *queue)
if (zfs_scan_issue_strategy == 1) {
return (range_tree_first(rt));
} else if (zfs_scan_issue_strategy == 2) {
+ /*
+ * We need to get the original entry in the by_addr
+ * tree so we can modify it.
+ */
range_seg_t *size_rs =
zfs_btree_first(&queue->q_exts_by_size, NULL);
+ if (size_rs == NULL)
+ return (NULL);
uint64_t start = rs_get_start(size_rs, rt);
uint64_t size = rs_get_end(size_rs, rt) - start;
range_seg_t *addr_rs = range_tree_find(rt, start,
size);
ASSERT3P(addr_rs, !=, NULL);
+ ASSERT3U(rs_get_start(size_rs, rt), ==,
+ rs_get_start(addr_rs, rt));
+ ASSERT3U(rs_get_end(size_rs, rt), ==,
+ rs_get_end(addr_rs, rt));
return (addr_rs);
}
}
@@ -2871,12 +2881,21 @@ scan_io_queue_fetch_ext(dsl_scan_io_queue_t *queue)
if (scn->scn_checkpointing) {
return (range_tree_first(rt));
} else if (scn->scn_clearing) {
+ /*
+ * We need to get the original entry in the by_addr
+ * tree so we can modify it.
+ */
range_seg_t *size_rs = zfs_btree_first(&queue->q_exts_by_size,
NULL);
+ if (size_rs == NULL)
+ return (NULL);
uint64_t start = rs_get_start(size_rs, rt);
uint64_t size = rs_get_end(size_rs, rt) - start;
range_seg_t *addr_rs = range_tree_find(rt, start, size);
ASSERT3P(addr_rs, !=, NULL);
+ ASSERT3U(rs_get_start(size_rs, rt), ==, rs_get_start(addr_rs,
+ rt));
+ ASSERT3U(rs_get_end(size_rs, rt), ==, rs_get_end(addr_rs, rt));
return (addr_rs);
} else {
return (NULL);
diff --git a/usr/src/uts/common/fs/zfs/range_tree.c b/usr/src/uts/common/fs/zfs/range_tree.c
index 8b670c6193..8eb1ba384b 100644
--- a/usr/src/uts/common/fs/zfs/range_tree.c
+++ b/usr/src/uts/common/fs/zfs/range_tree.c
@@ -349,7 +349,7 @@ range_tree_add_impl(void *arg, uint64_t start, uint64_t size, uint64_t fill)
uint64_t before_start = rs_get_start_raw(rs_before, rt);
uint64_t before_fill = rs_get_fill(rs_before, rt);
uint64_t after_fill = rs_get_fill(rs_after, rt);
- zfs_btree_remove_from(&rt->rt_root, &where_before);
+ zfs_btree_remove_idx(&rt->rt_root, &where_before);
/*
* We have to re-find the node because our old reference is
@@ -385,7 +385,7 @@ range_tree_add_impl(void *arg, uint64_t start, uint64_t size, uint64_t fill)
rs_set_start(rs, rt, start);
rs_set_end(rs, rt, end);
rs_set_fill(rs, rt, fill);
- zfs_btree_insert(&rt->rt_root, rs, &where);
+ zfs_btree_add_idx(&rt->rt_root, rs, &where);
}
if (gap != 0) {
@@ -488,7 +488,7 @@ range_tree_remove_impl(range_tree_t *rt, uint64_t start, uint64_t size,
rs_copy(rs, &rs_tmp, rt);
if (zfs_btree_next(&rt->rt_root, &where, &where) != NULL)
- zfs_btree_insert(&rt->rt_root, &newseg, &where);
+ zfs_btree_add_idx(&rt->rt_root, &newseg, &where);
else
zfs_btree_add(&rt->rt_root, &newseg);
@@ -503,7 +503,7 @@ range_tree_remove_impl(range_tree_t *rt, uint64_t start, uint64_t size,
rs_set_start(rs, rt, end);
rs_copy(rs, &rs_tmp, rt);
} else {
- zfs_btree_remove_from(&rt->rt_root, &where);
+ zfs_btree_remove_idx(&rt->rt_root, &where);
rs = NULL;
}
diff --git a/usr/src/uts/common/fs/zfs/sys/btree.h b/usr/src/uts/common/fs/zfs/sys/btree.h
index 432a6c6886..e518565c83 100644
--- a/usr/src/uts/common/fs/zfs/sys/btree.h
+++ b/usr/src/uts/common/fs/zfs/sys/btree.h
@@ -129,10 +129,10 @@ void zfs_btree_create(zfs_btree_t *, int (*) (const void *, const void *),
/*
* Find a node with a matching value in the tree. Returns the matching node
* found. If not found, it returns NULL and then if "where" is not NULL it sets
- * "where" for use with zfs_btree_insert() or zfs_btree_nearest().
+ * "where" for use with zfs_btree_add_idx() or zfs_btree_nearest().
*
* node - node that has the value being looked for
- * where - position for use with zfs_btree_nearest() or zfs_btree_insert(),
+ * where - position for use with zfs_btree_nearest() or zfs_btree_add_idx(),
* may be NULL
*/
void *zfs_btree_find(zfs_btree_t *, const void *, zfs_btree_index_t *);
@@ -143,17 +143,20 @@ void *zfs_btree_find(zfs_btree_t *, const void *, zfs_btree_index_t *);
* node - the node to insert
* where - position as returned from zfs_btree_find()
*/
-void zfs_btree_insert(zfs_btree_t *, const void *, const zfs_btree_index_t *);
+void zfs_btree_add_idx(zfs_btree_t *, const void *, const zfs_btree_index_t *);
/*
- * Return the first or last valued node in the tree. Will return NULL
- * if the tree is empty.
+ * Return the first or last valued node in the tree. Will return NULL if the
+ * tree is empty. The index can be NULL if the location of the first or last
+ * element isn't required.
*/
void *zfs_btree_first(zfs_btree_t *, zfs_btree_index_t *);
void *zfs_btree_last(zfs_btree_t *, zfs_btree_index_t *);
/*
- * Return the next or previous valued node in the tree.
+ * Return the next or previous valued node in the tree. The second index can
+ * safely be NULL, if the location of the next or previous value isn't
+ * required.
*/
void *zfs_btree_next(zfs_btree_t *, const zfs_btree_index_t *,
zfs_btree_index_t *);
@@ -167,7 +170,9 @@ void *zfs_btree_get(zfs_btree_t *, zfs_btree_index_t *);
/*
* Add a single value to the tree. The value must not compare equal to any
- * other node already in the tree.
+ * other node already in the tree. Note that the value will be copied out, not
+ * inserted directly. It is safe to free or destroy the value once this
+ * function returns.
*/
void zfs_btree_add(zfs_btree_t *, const void *);
@@ -181,7 +186,7 @@ void zfs_btree_remove(zfs_btree_t *, const void *);
/*
* Remove the value at the given location from the tree.
*/
-void zfs_btree_remove_from(zfs_btree_t *, zfs_btree_index_t *);
+void zfs_btree_remove_idx(zfs_btree_t *, zfs_btree_index_t *);
/*
* Return the number of nodes in the tree
diff --git a/usr/src/uts/common/fs/zfs/sys/vdev_disk.h b/usr/src/uts/common/fs/zfs/sys/vdev_disk.h
deleted file mode 100644
index 61e2f273f0..0000000000
--- a/usr/src/uts/common/fs/zfs/sys/vdev_disk.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- * Copyright (c) 2013 Joyent, Inc. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
- */
-
-#ifndef _SYS_VDEV_DISK_H
-#define _SYS_VDEV_DISK_H
-
-#include <sys/vdev.h>
-#ifdef _KERNEL
-#include <sys/buf.h>
-#include <sys/ddi.h>
-#include <sys/sunldi.h>
-#include <sys/sunddi.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef _KERNEL
-typedef struct vdev_disk {
- ddi_devid_t vd_devid;
- char *vd_minor;
- ldi_handle_t vd_lh;
- list_t vd_ldi_cbs;
- boolean_t vd_ldi_offline;
-} vdev_disk_t;
-#endif
-
-extern int vdev_disk_physio(vdev_t *,
- caddr_t, size_t, uint64_t, int, boolean_t);
-
-/*
- * Since vdev_disk.c is not compiled into libzpool, this function should only be
- * defined in the zfs kernel module.
- */
-#ifdef _KERNEL
-extern int vdev_disk_ldi_physio(ldi_handle_t, caddr_t, size_t, uint64_t, int);
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_VDEV_DISK_H */
diff --git a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
index bc676903bc..fccd0a6784 100644
--- a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
+++ b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
@@ -83,6 +83,8 @@ typedef void vdev_remap_cb_t(uint64_t inner_offset, vdev_t *vd,
uint64_t offset, uint64_t size, void *arg);
typedef void vdev_remap_func_t(vdev_t *vd, uint64_t offset, uint64_t size,
vdev_remap_cb_t callback, void *arg);
+typedef int vdev_dumpio_func_t(vdev_t *vd, caddr_t data, size_t size,
+ uint64_t offset, uint64_t origoffset, boolean_t doread, boolean_t isdump);
/*
* Given a target vdev, translates the logical range "in" to the physical
* range "res"
@@ -106,6 +108,7 @@ typedef struct vdev_ops {
* Used when initializing vdevs. Isn't used by leaf ops.
*/
vdev_xlation_func_t *vdev_op_xlate;
+ vdev_dumpio_func_t *vdev_op_dumpio;
char vdev_op_type[16];
boolean_t vdev_op_leaf;
} vdev_ops_t;
diff --git a/usr/src/uts/common/fs/zfs/sys/vdev_raidz.h b/usr/src/uts/common/fs/zfs/sys/vdev_raidz.h
deleted file mode 100644
index 579d272cdf..0000000000
--- a/usr/src/uts/common/fs/zfs/sys/vdev_raidz.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
- */
-
-#ifndef _SYS_VDEV_RAIDZ_H
-#define _SYS_VDEV_RAIDZ_H
-
-#include <sys/vdev.h>
-#include <sys/semaphore.h>
-#ifdef _KERNEL
-#include <sys/ddi.h>
-#include <sys/sunldi.h>
-#include <sys/sunddi.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef _KERNEL
-extern int vdev_raidz_physio(vdev_t *,
- caddr_t, size_t, uint64_t, uint64_t, boolean_t, boolean_t);
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_VDEV_RAIDZ_H */
diff --git a/usr/src/uts/common/fs/zfs/vdev_disk.c b/usr/src/uts/common/fs/zfs/vdev_disk.c
index 20df3f306b..f8604896ca 100644
--- a/usr/src/uts/common/fs/zfs/vdev_disk.c
+++ b/usr/src/uts/common/fs/zfs/vdev_disk.c
@@ -29,7 +29,6 @@
#include <sys/zfs_zone.h>
#include <sys/spa_impl.h>
#include <sys/refcount.h>
-#include <sys/vdev_disk.h>
#include <sys/vdev_impl.h>
#include <sys/vdev_trim.h>
#include <sys/abd.h>
@@ -38,6 +37,7 @@
#include <sys/sunldi.h>
#include <sys/efi_partition.h>
#include <sys/fm/fs/zfs.h>
+#include <sys/ddi.h>
/*
* Tunable to disable TRIM in case we're using a problematic SSD.
@@ -59,6 +59,14 @@ extern ldi_ident_t zfs_li;
static void vdev_disk_close(vdev_t *);
+typedef struct vdev_disk {
+ ddi_devid_t vd_devid;
+ char *vd_minor;
+ ldi_handle_t vd_lh;
+ list_t vd_ldi_cbs;
+ boolean_t vd_ldi_offline;
+} vdev_disk_t;
+
typedef struct vdev_disk_buf {
buf_t vdb_buf;
zio_t *vdb_io;
@@ -775,35 +783,7 @@ vdev_disk_close(vdev_t *vd)
vdev_disk_free(vd);
}
-int
-vdev_disk_physio(vdev_t *vd, caddr_t data,
- size_t size, uint64_t offset, int flags, boolean_t isdump)
-{
- vdev_disk_t *dvd = vd->vdev_tsd;
-
- /*
- * If the vdev is closed, it's likely in the REMOVED or FAULTED state.
- * Nothing to be done here but return failure.
- */
- if (dvd == NULL || (dvd->vd_ldi_offline && dvd->vd_lh == NULL))
- return (EIO);
-
- ASSERT(vd->vdev_ops == &vdev_disk_ops);
-
- /*
- * If in the context of an active crash dump, use the ldi_dump(9F)
- * call instead of ldi_strategy(9F) as usual.
- */
- if (isdump) {
- ASSERT3P(dvd, !=, NULL);
- return (ldi_dump(dvd->vd_lh, data, lbtodb(offset),
- lbtodb(size)));
- }
-
- return (vdev_disk_ldi_physio(dvd->vd_lh, data, size, offset, flags));
-}
-
-int
+static int
vdev_disk_ldi_physio(ldi_handle_t vd_lh, caddr_t data,
size_t size, uint64_t offset, int flags)
{
@@ -832,6 +812,40 @@ vdev_disk_ldi_physio(ldi_handle_t vd_lh, caddr_t data,
}
static int
+vdev_disk_dumpio(vdev_t *vd, caddr_t data, size_t size,
+ uint64_t offset, uint64_t origoffset, boolean_t doread, boolean_t isdump)
+{
+ vdev_disk_t *dvd = vd->vdev_tsd;
+ int flags = doread ? B_READ : B_WRITE;
+
+ /*
+ * If the vdev is closed, it's likely in the REMOVED or FAULTED state.
+ * Nothing to be done here but return failure.
+ *
+ * XXX-mg there is still a race here with off_notify
+ */
+ if (dvd == NULL || dvd->vd_ldi_offline) {
+ return (EIO);
+ }
+
+ ASSERT(vd->vdev_ops == &vdev_disk_ops);
+
+ offset += VDEV_LABEL_START_SIZE;
+
+ /*
+ * If in the context of an active crash dump, use the ldi_dump(9F)
+ * call instead of ldi_strategy(9F) as usual.
+ */
+ if (isdump) {
+ ASSERT3P(dvd, !=, NULL);
+ return (ldi_dump(dvd->vd_lh, data, lbtodb(offset),
+ lbtodb(size)));
+ }
+
+ return (vdev_disk_ldi_physio(dvd->vd_lh, data, size, offset, flags));
+}
+
+static int
vdev_disk_io_intr(buf_t *bp)
{
vdev_buf_t *vb = (vdev_buf_t *)bp;
@@ -1070,6 +1084,7 @@ vdev_ops_t vdev_disk_ops = {
.vdev_op_rele = vdev_disk_rele,
.vdev_op_remap = NULL,
.vdev_op_xlate = vdev_default_xlate,
+ .vdev_op_dumpio = vdev_disk_dumpio,
.vdev_op_type = VDEV_TYPE_DISK, /* name of this vdev type */
.vdev_op_leaf = B_TRUE /* leaf vdev */
};
diff --git a/usr/src/uts/common/fs/zfs/vdev_file.c b/usr/src/uts/common/fs/zfs/vdev_file.c
index 6ce7e714a7..b13d24a4e7 100644
--- a/usr/src/uts/common/fs/zfs/vdev_file.c
+++ b/usr/src/uts/common/fs/zfs/vdev_file.c
@@ -303,6 +303,7 @@ vdev_ops_t vdev_file_ops = {
.vdev_op_rele = vdev_file_rele,
.vdev_op_remap = NULL,
.vdev_op_xlate = vdev_default_xlate,
+ .vdev_op_dumpio = NULL,
.vdev_op_type = VDEV_TYPE_FILE, /* name of this vdev type */
.vdev_op_leaf = B_TRUE /* leaf vdev */
};
@@ -324,6 +325,7 @@ vdev_ops_t vdev_disk_ops = {
.vdev_op_rele = vdev_file_rele,
.vdev_op_remap = NULL,
.vdev_op_xlate = vdev_default_xlate,
+ .vdev_op_dumpio = NULL,
.vdev_op_type = VDEV_TYPE_DISK, /* name of this vdev type */
.vdev_op_leaf = B_TRUE /* leaf vdev */
};
diff --git a/usr/src/uts/common/fs/zfs/vdev_indirect.c b/usr/src/uts/common/fs/zfs/vdev_indirect.c
index 493e2b51ed..effea61bc6 100644
--- a/usr/src/uts/common/fs/zfs/vdev_indirect.c
+++ b/usr/src/uts/common/fs/zfs/vdev_indirect.c
@@ -15,6 +15,7 @@
/*
* Copyright (c) 2014, 2019 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/zfs_context.h>
@@ -1830,6 +1831,7 @@ vdev_ops_t vdev_indirect_ops = {
.vdev_op_rele = NULL,
.vdev_op_remap = vdev_indirect_remap,
.vdev_op_xlate = NULL,
+ .vdev_op_dumpio = NULL,
.vdev_op_type = VDEV_TYPE_INDIRECT, /* name of this vdev type */
.vdev_op_leaf = B_FALSE /* leaf vdev */
};
diff --git a/usr/src/uts/common/fs/zfs/vdev_mirror.c b/usr/src/uts/common/fs/zfs/vdev_mirror.c
index f654bf9afb..5758bf8bd4 100644
--- a/usr/src/uts/common/fs/zfs/vdev_mirror.c
+++ b/usr/src/uts/common/fs/zfs/vdev_mirror.c
@@ -25,6 +25,7 @@
/*
* Copyright (c) 2012, 2018 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/zfs_context.h>
@@ -781,6 +782,35 @@ vdev_mirror_state_change(vdev_t *vd, int faulted, int degraded)
}
}
+static int
+vdev_mirror_dumpio(vdev_t *vd, caddr_t data, size_t size,
+ uint64_t offset, uint64_t origoffset, boolean_t doread, boolean_t isdump)
+{
+ uint64_t numerrors;
+ int err = EIO;
+
+ for (uint64_t c = 0; c < vd->vdev_children; c++) {
+ vdev_t *cvd = vd->vdev_child[c];
+
+ if (cvd->vdev_ops->vdev_op_dumpio == NULL) {
+ err = EINVAL;
+ } else {
+ err = cvd->vdev_ops->vdev_op_dumpio(cvd, data, size,
+ offset, origoffset, doread, isdump);
+ }
+ if (err != 0) {
+ numerrors++;
+ } else if (doread) {
+ break;
+ }
+ }
+ if (err != 0) {
+ return (SET_ERROR(err));
+ }
+
+ return (0);
+}
+
vdev_ops_t vdev_mirror_ops = {
.vdev_op_open = vdev_mirror_open,
.vdev_op_close = vdev_mirror_close,
@@ -793,6 +823,7 @@ vdev_ops_t vdev_mirror_ops = {
.vdev_op_rele = NULL,
.vdev_op_remap = NULL,
.vdev_op_xlate = vdev_default_xlate,
+ .vdev_op_dumpio = vdev_mirror_dumpio,
.vdev_op_type = VDEV_TYPE_MIRROR, /* name of this vdev type */
.vdev_op_leaf = B_FALSE /* not a leaf vdev */
};
@@ -809,6 +840,7 @@ vdev_ops_t vdev_replacing_ops = {
.vdev_op_rele = NULL,
.vdev_op_remap = NULL,
.vdev_op_xlate = vdev_default_xlate,
+ .vdev_op_dumpio = vdev_mirror_dumpio,
.vdev_op_type = VDEV_TYPE_REPLACING, /* name of this vdev type */
.vdev_op_leaf = B_FALSE /* not a leaf vdev */
};
@@ -825,6 +857,7 @@ vdev_ops_t vdev_spare_ops = {
.vdev_op_rele = NULL,
.vdev_op_remap = NULL,
.vdev_op_xlate = vdev_default_xlate,
+ .vdev_op_dumpio = vdev_mirror_dumpio,
.vdev_op_type = VDEV_TYPE_SPARE, /* name of this vdev type */
.vdev_op_leaf = B_FALSE /* not a leaf vdev */
};
diff --git a/usr/src/uts/common/fs/zfs/vdev_missing.c b/usr/src/uts/common/fs/zfs/vdev_missing.c
index 205b23eba7..b436762c39 100644
--- a/usr/src/uts/common/fs/zfs/vdev_missing.c
+++ b/usr/src/uts/common/fs/zfs/vdev_missing.c
@@ -25,6 +25,7 @@
/*
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -91,6 +92,7 @@ vdev_ops_t vdev_missing_ops = {
.vdev_op_rele = NULL,
.vdev_op_remap = NULL,
.vdev_op_xlate = NULL,
+ .vdev_op_dumpio = NULL,
.vdev_op_type = VDEV_TYPE_MISSING, /* name of this vdev type */
.vdev_op_leaf = B_TRUE /* leaf vdev */
};
@@ -107,6 +109,7 @@ vdev_ops_t vdev_hole_ops = {
.vdev_op_rele = NULL,
.vdev_op_remap = NULL,
.vdev_op_xlate = NULL,
+ .vdev_op_dumpio = NULL,
.vdev_op_type = VDEV_TYPE_HOLE, /* name of this vdev type */
.vdev_op_leaf = B_TRUE /* leaf vdev */
};
diff --git a/usr/src/uts/common/fs/zfs/vdev_raidz.c b/usr/src/uts/common/fs/zfs/vdev_raidz.c
index aaa87b9d3c..10772d5265 100644
--- a/usr/src/uts/common/fs/zfs/vdev_raidz.c
+++ b/usr/src/uts/common/fs/zfs/vdev_raidz.c
@@ -22,16 +22,14 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2019 by Delphix. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
* Copyright (c) 2014 Integros [integros.com]
*/
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/vdev_impl.h>
-#include <sys/vdev_disk.h>
#include <sys/vdev_file.h>
-#include <sys/vdev_raidz.h>
#include <sys/zio.h>
#include <sys/zio_checksum.h>
#include <sys/abd.h>
@@ -1763,7 +1761,7 @@ vdev_raidz_close(vdev_t *vd)
*
* However, when writing to the dump device, the behavior is different:
*
- * vdev_raidz_physio(data, size: 32 KB, offset: 64 KB)
+ * vdev_raidz_dumpio(data, size: 32 KB, offset: 64 KB)
*
* Unlike the normal RAID-Z case in which the block is allocated based on the
* I/O size, reads and writes here always use a 128 KB logical I/O size. If the
@@ -1791,8 +1789,8 @@ vdev_raidz_close(vdev_t *vd)
* |
* 32 KB data block
*/
-int
-vdev_raidz_physio(vdev_t *vd, caddr_t data, size_t size,
+static int
+vdev_raidz_dumpio(vdev_t *vd, caddr_t data, size_t size,
uint64_t offset, uint64_t origoffset, boolean_t doread, boolean_t isdump)
{
vdev_t *tvd = vd->vdev_top;
@@ -1804,8 +1802,6 @@ vdev_raidz_physio(vdev_t *vd, caddr_t data, size_t size,
uint64_t start, end, colstart, colend;
uint64_t coloffset, colsize, colskip;
- int flags = doread ? B_READ : B_WRITE;
-
#ifdef _KERNEL
/*
@@ -1838,6 +1834,11 @@ vdev_raidz_physio(vdev_t *vd, caddr_t data, size_t size,
rc = &rm->rm_col[c];
cvd = vd->vdev_child[rc->rc_devidx];
+ if (cvd->vdev_ops->vdev_op_dumpio == NULL) {
+ err = EINVAL;
+ break;
+ }
+
/*
* Find the start and end of this column in the RAID-Z map,
* keeping in mind that the stated size and offset of the
@@ -1865,10 +1866,10 @@ vdev_raidz_physio(vdev_t *vd, caddr_t data, size_t size,
* VDEV_LABEL_OFFSET(). See zio_vdev_child_io() for another
* example of why this calculation is needed.
*/
- if ((err = vdev_disk_physio(cvd,
+ if ((err = cvd->vdev_ops->vdev_op_dumpio(cvd,
((char *)abd_to_buf(rc->rc_abd)) + colskip, colsize,
- VDEV_LABEL_OFFSET(rc->rc_offset) + colskip,
- flags, isdump)) != 0)
+ VDEV_LABEL_OFFSET(rc->rc_offset) + colskip, 0,
+ doread, isdump)) != 0)
break;
}
@@ -2697,6 +2698,7 @@ vdev_ops_t vdev_raidz_ops = {
.vdev_op_rele = NULL,
.vdev_op_remap = NULL,
.vdev_op_xlate = vdev_raidz_xlate,
+ .vdev_op_dumpio = vdev_raidz_dumpio,
.vdev_op_type = VDEV_TYPE_RAIDZ, /* name of this vdev type */
.vdev_op_leaf = B_FALSE /* not a leaf vdev */
};
diff --git a/usr/src/uts/common/fs/zfs/vdev_root.c b/usr/src/uts/common/fs/zfs/vdev_root.c
index 7170f70136..5e8875ae73 100644
--- a/usr/src/uts/common/fs/zfs/vdev_root.c
+++ b/usr/src/uts/common/fs/zfs/vdev_root.c
@@ -25,6 +25,7 @@
/*
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/zfs_context.h>
@@ -151,6 +152,7 @@ vdev_ops_t vdev_root_ops = {
.vdev_op_rele = NULL,
.vdev_op_remap = NULL,
.vdev_op_xlate = NULL,
+ .vdev_op_dumpio = NULL,
.vdev_op_type = VDEV_TYPE_ROOT, /* name of this vdev type */
.vdev_op_leaf = B_FALSE /* not a leaf vdev */
};
diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c
index 0b625e0365..3ed5977c20 100644
--- a/usr/src/uts/common/fs/zfs/zvol.c
+++ b/usr/src/uts/common/fs/zfs/zvol.c
@@ -78,9 +78,7 @@
#include <sys/refcount.h>
#include <sys/zfs_znode.h>
#include <sys/zfs_rlock.h>
-#include <sys/vdev_disk.h>
#include <sys/vdev_impl.h>
-#include <sys/vdev_raidz.h>
#include <sys/zvol.h>
#include <sys/dumphdr.h>
#include <sys/zil_impl.h>
@@ -1135,53 +1133,15 @@ static int
zvol_dumpio_vdev(vdev_t *vd, void *addr, uint64_t offset, uint64_t origoffset,
uint64_t size, boolean_t doread, boolean_t isdump)
{
- vdev_disk_t *dvd;
- int c;
- int numerrors = 0;
-
- if (vd->vdev_ops == &vdev_mirror_ops ||
- vd->vdev_ops == &vdev_replacing_ops ||
- vd->vdev_ops == &vdev_spare_ops) {
- for (c = 0; c < vd->vdev_children; c++) {
- int err = zvol_dumpio_vdev(vd->vdev_child[c],
- addr, offset, origoffset, size, doread, isdump);
- if (err != 0) {
- numerrors++;
- } else if (doread) {
- break;
- }
- }
- }
-
- if (!vd->vdev_ops->vdev_op_leaf && vd->vdev_ops != &vdev_raidz_ops)
- return (numerrors < vd->vdev_children ? 0 : EIO);
-
if (doread && !vdev_readable(vd))
return (SET_ERROR(EIO));
- else if (!doread && !vdev_writeable(vd))
+ if (!doread && !vdev_writeable(vd))
+ return (SET_ERROR(EIO));
+ if (vd->vdev_ops->vdev_op_dumpio == NULL)
return (SET_ERROR(EIO));
- if (vd->vdev_ops == &vdev_raidz_ops) {
- return (vdev_raidz_physio(vd,
- addr, size, offset, origoffset, doread, isdump));
- }
-
- offset += VDEV_LABEL_START_SIZE;
-
- if (ddi_in_panic() || isdump) {
- ASSERT(!doread);
- if (doread)
- return (SET_ERROR(EIO));
- dvd = vd->vdev_tsd;
- ASSERT3P(dvd, !=, NULL);
- return (ldi_dump(dvd->vd_lh, addr, lbtodb(offset),
- lbtodb(size)));
- } else {
- dvd = vd->vdev_tsd;
- ASSERT3P(dvd, !=, NULL);
- return (vdev_disk_ldi_physio(dvd->vd_lh, addr, size,
- offset, doread ? B_READ : B_WRITE));
- }
+ return (vd->vdev_ops->vdev_op_dumpio(vd, addr, size,
+ offset, origoffset, doread, isdump));
}
static int
@@ -1234,7 +1194,7 @@ zvol_strategy(buf_t *bp)
char *addr;
objset_t *os;
int error = 0;
- boolean_t doread = bp->b_flags & B_READ;
+ boolean_t doread = !!(bp->b_flags & B_READ);
boolean_t is_dumpified;
boolean_t sync;
diff --git a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c
index a786bbb051..e458c61168 100644
--- a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c
+++ b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c
@@ -14898,10 +14898,12 @@ mptsas_update_hashtab(struct mptsas *mpt)
rval = mptsas_get_target_device_info(mpt, page_address,
&dev_handle, &ptgt);
if ((rval == DEV_INFO_FAIL_PAGE0) ||
- (rval == DEV_INFO_FAIL_ALLOC) ||
- (rval == DEV_INFO_FAIL_GUID)) {
+ (rval == DEV_INFO_FAIL_ALLOC)) {
break;
}
+ if (rval == DEV_INFO_FAIL_GUID) {
+ continue;
+ }
mpt->m_dev_handle = dev_handle;
}
@@ -16601,12 +16603,12 @@ mptsas_phy_to_tgt(mptsas_t *mpt, mptsas_phymask_t phymask, uint8_t phy)
rval = mptsas_get_target_device_info(mpt, page_address,
&cur_handle, &ptgt);
if ((rval == DEV_INFO_FAIL_PAGE0) ||
- (rval == DEV_INFO_FAIL_ALLOC) ||
- (rval == DEV_INFO_FAIL_GUID)) {
+ (rval == DEV_INFO_FAIL_ALLOC)) {
break;
}
if ((rval == DEV_INFO_WRONG_DEVICE_TYPE) ||
- (rval == DEV_INFO_PHYS_DISK)) {
+ (rval == DEV_INFO_PHYS_DISK) ||
+ (rval == DEV_INFO_FAIL_GUID)) {
continue;
}
mpt->m_dev_handle = cur_handle;
@@ -16671,13 +16673,13 @@ mptsas_wwid_to_ptgt(mptsas_t *mpt, mptsas_phymask_t phymask, uint64_t wwid)
rval = mptsas_get_target_device_info(mpt, page_address,
&cur_handle, &tmp_tgt);
if ((rval == DEV_INFO_FAIL_PAGE0) ||
- (rval == DEV_INFO_FAIL_ALLOC) ||
- (rval == DEV_INFO_FAIL_GUID)) {
+ (rval == DEV_INFO_FAIL_ALLOC)) {
tmp_tgt = NULL;
break;
}
if ((rval == DEV_INFO_WRONG_DEVICE_TYPE) ||
- (rval == DEV_INFO_PHYS_DISK)) {
+ (rval == DEV_INFO_PHYS_DISK) ||
+ (rval == DEV_INFO_FAIL_GUID)) {
continue;
}
mpt->m_dev_handle = cur_handle;
diff --git a/usr/src/uts/common/io/usb/clients/ccid/ccid.c b/usr/src/uts/common/io/usb/clients/ccid/ccid.c
new file mode 100644
index 0000000000..22578d4a1c
--- /dev/null
+++ b/usr/src/uts/common/io/usb/clients/ccid/ccid.c
@@ -0,0 +1,4389 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * USB CCID class driver
+ *
+ * Slot Detection
+ * --------------
+ *
+ * A CCID reader has one or more slots, each of which may or may not have a ICC
+ * (integrated circuit card) present. Some readers actually have a card that's
+ * permanently plugged in while other readers allow for cards to be inserted and
+ * removed. We model all CCID readers that don't have removable cards as ones
+ * that are removable, but never fire any events. Readers with removable cards
+ * are required to have an Interrupt-IN pipe.
+ *
+ * Each slot starts in an unknown state. After attaching we always kick off a
+ * discovery. When a change event comes in, that causes us to kick off a
+ * discovery again, though we focus it only on those endpoints that have noted a
+ * change. At attach time we logically mark that every endpoint has changed,
+ * allowing us to figure out what its actual state is. We don't rely on any
+ * initial Interrupt-IN polling to allow for the case where either the hardware
+ * doesn't report it or to better handle the devices without an Interrupt-IN
+ * entry. Just because we open up the Interrupt-IN pipe, hardware is not
+ * obligated to tell us, as the detaching and reattaching of a driver will not
+ * cause a power cycle.
+ *
+ * The Interrupt-IN exception callback may need to restart polling. In addition,
+ * we may fail to start or restart polling due to a transient issue. In cases
+ * where the attempt to start polling has failed, we try again in one second
+ * with a timeout.
+ *
+ * Discovery is run through a taskq. The various slots are checked serially. If
+ * a discovery is running when another change event comes in, we flag ourselves
+ * for a follow up run. This means that it's possible that we end up processing
+ * items early and that the follow up run is ignored.
+ *
+ * Two state flags are used to keep track of this dance: CCID_F_WORKER_REQUESTED
+ * and CCID_F_WORKER_RUNNING. The first is used to indicate that discovery is
+ * desired. The second is to indicate that it is actively running. When
+ * discovery is requested, the caller first checks the current flags. If neither
+ * flag is set, then it knows that it can kick off discovery. Regardless if it
+ * can kick off the taskq, it always sets requested. Once the taskq entry
+ * starts, it removes any DISCOVER_REQUESTED flags and sets DISCOVER_RUNNING. If
+ * at the end of discovery, we find that another request has been made, the
+ * discovery function will kick off another entry in the taskq.
+ *
+ * The one possible problem with this model is that it means that we aren't
+ * throttling the set of incoming requests with respect to taskq dispatches.
+ * However, because these are only driven by an Interrupt-IN pipe, it is hoped
+ * that the frequency will be rather reduced. If it turns out that that's not
+ * the case, we may need to use a timeout or another trick to ensure that only
+ * one discovery per tick or so is initialized. The main reason we don't just do
+ * that off the bat and add a delay is because of contactless cards which may
+ * need to be acted upon in a soft real-time fashion.
+ *
+ * Command Handling
+ * ----------------
+ *
+ * Commands are issued to a CCID reader on a Bulk-OUT pipe. Responses are
+ * generated as a series of one or more messages on a Bulk-IN pipe. To correlate
+ * these commands a sequence number is used. This sequence number is one byte
+ * and can be in the range [ CCID_SEQ_MIN, CCID_SEQ_MAX ]. To keep track of the
+ * allocated IDs we leverage an ID space.
+ *
+ * A CCID reader contains a number of slots. Each slot can be addressed
+ * separately as each slot represents a separate place that a card may be
+ * inserted or not. A given slot may only have a single outstanding command. A
+ * given CCID reader may only have a number of commands outstanding to the CCID
+ * device as a whole based on a value in the class descriptor (see the
+ * ccd_bMacCCIDBusySlots member of the ccid_class_descr_t).
+ *
+ * To simplify the driver, we only support issuing a single command to a CCID
+ * reader at any given time. All commands that are outstanding are queued in a
+ * per-device list ccid_command_queue. The head of the queue is the current
+ * command that we believe is outstanding to the reader or will be shortly. The
+ * command is issued by sending a Bulk-OUT request with a CCID header. Once we
+ * have the Bulk-OUT request acknowledged, we begin sending Bulk-IN messages to
+ * the controller. Once the Bulk-IN message is acknowledged, then we complete
+ * the command and proceed to the next command. This is summarized in the
+ * following state machine:
+ *
+ * +-----------------------------------------------------+
+ * | |
+ * | ccid_command_queue |
+ * | +---+---+---------+---+---+ |
+ * v | h | | | | t | |
+ * +-------------------------+ | e | | | | a | |
+ * | ccid_command_dispatch() |<-----| a | | ... | | i | |
+ * +-----------+-------------+ | d | | | | l | |
+ * | +---+---+---------+---+---+ |
+ * v |
+ * +-------------------------+ +-------------------------+ |
+ * | usb_pipe_bulk_xfer() |----->| ccid_dispatch_bulk_cb() | |
+ * | ccid_bulkout_pipe | +------------+------------+ |
+ * +-------------------------+ | |
+ * | |
+ * | v |
+ * | +-------------------------+ |
+ * | | ccid_bulkin_schedule() | |
+ * v +------------+------------+ |
+ * | |
+ * /--------------------\ | |
+ * / \ v |
+ * | ### CCID HW | +-------------------------+ |
+ * | ### | | usb_pipe_bulk_xfer() | |
+ * | | ----> | ccid_bulkin_pipe | |
+ * | | +------------+------------+ |
+ * \ / | |
+ * \--------------------/ | |
+ * v |
+ * +-------------------------+ |
+ * | ccid_reply_bulk_cb() | |
+ * +------------+------------+ |
+ * | |
+ * | |
+ * v |
+ * +-------------------------+ |
+ * | ccid_command_complete() +------+
+ * +-------------------------+
+ *
+ *
+ * APDU and TPDU Processing and Parameter Selection
+ * ------------------------------------------------
+ *
+ * Readers provide four different modes for us to be able to transmit data to
+ * and from the card. These are:
+ *
+ * 1. Character Mode 2. TPDU Mode 3. Short APDU Mode 4. Extended APDU Mode
+ *
+ * Readers either support mode 1, mode 2, mode 3, or mode 3 and 4. All readers
+ * that support extended APDUs support short APDUs. At this time, we do not
+ * support character mode or TPDU mode, and we use only short APDUs even for
+ * readers that support extended APDUs.
+ *
+ * The ICC and the reader need to be in agreement in order for them to be able
+ * to exchange information. The ICC indicates what it supports by replying to a
+ * power on command with an ATR (answer to reset). This data can be parsed to
+ * indicate which of two protocols the ICC supports. These protocols are
+ * referred to as:
+ *
+ * o T=0
+ * o T=1
+ *
+ * These protocols are defined in the ISO/IEC 7816-3:2006 specification. When a
+ * reader supports an APDU mode, then the driver does not have to worry about
+ * the underlying protocol and can just send an application data unit (APDU).
+ * Otherwise, the driver must take the application data (APDU) and transform it
+ * into the form required by the corresponding protocol.
+ *
+ * There are several parameters that need to be negotiated to ensure that the
+ * protocols work correctly. To negotiate these parameters and to select a
+ * protocol, the driver must construct a PPS (protocol and parameters structure)
+ * request and exchange that with the ICC. A reader may optionally take care of
+ * performing this and indicates its support for this in dwFeatures member of
+ * the USB class descriptor.
+ *
+ * In addition, the reader itself must often be told of these configuration
+ * changes through the means of a CCID_REQUEST_SET_PARAMS command. Once both of
+ * these have been performed, the reader and ICC can communicate to their hearts
+ * desire.
+ *
+ * Both the negotiation and the setting of the parameters can be performed
+ * automatically by the CCID reader. When the reader supports APDU exchanges,
+ * then it must support some aspects of this negotiation. Because of that, we
+ * never consider performing this and only support readers that offer this kind
+ * of automation.
+ *
+ * User I/O Basics
+ * ---------------
+ *
+ * A user performs I/O by writing APDUs (Application Protocol Data Units). A
+ * user issues a system call that ends up in write(9E) (write(2), writev(2),
+ * pwrite(2), pwritev(2), etc.). The user data is consumed by the CCID driver
+ * and a series of commands will then be issued to the device, depending on the
+ * protocol mode. The write(9E) call does not block for this to finish. Once
+ * write(9E) has returned, the user may block in a read(2) related system call
+ * or poll for POLLIN.
+ *
+ * A thread may not call read(9E) without having called write(9E). This model is
+ * due to the limited capability of hardware. Only a single command can be going
+ * on a given slot and due to the fact that many commands change the hardware
+ * state, we do not try to multiplex multiple calls to write() or read().
+ *
+ *
+ * User I/O, Transaction Ends, ICC removals, and Reader Removals
+ * -------------------------------------------------------------
+ *
+ * While the I/O model given to user land is somewhat simple, there are a lot of
+ * tricky pieces to get right because we are in a multi-threaded pre-emptible
+ * system. In general, there are four different levels of state that we need to
+ * keep track of:
+ *
+ * 1. User threads in I/O
+ * 2. Kernel protocol level support (T=1, apdu, etc.).
+ * 3. Slot/ICC state
+ * 4. CCID Reader state
+ *
+ * Of course, each level cares about the state above it. The kernel protocol
+ * level state (2) cares about the User threads in I/O (1). The same is true
+ * with the other levels caring about the levels above it. With this in mind
+ * there are three non-data path things that can go wrong:
+ *
+ * A. The user can end a transaction (whether through an ioctl or close(9E)).
+ * B. The ICC can be removed
+ * C. The CCID device can be removed or reset at a USB level.
+ *
+ * Each of these has implications on the outstanding I/O and other states of
+ * the world. When events of type A occur, we need to clean up states 1 and 2.
+ * Then events of type B occur we need to clean up states 1-3. When events of
+ * type C occur we need to clean up states 1-4. The following discusses how we
+ * should clean up these different states:
+ *
+ * Cleaning up State 1:
+ *
+ * To clean up the User threads in I/O there are three different cases to
+ * consider. The first is cleaning up a thread that is in the middle of
+ * write(9E). The second is cleaning up thread that is blocked in read(9E).
+ * The third is dealing with threads that are stuck in chpoll(9E).
+ *
+ * To handle the write case, we have a series of flags that is on the CCID
+ * slot's I/O structure (ccid_io_t, cs_io on the ccid_slot_t). When a thread
+ * begins its I/O it will set the CCID_IO_F_PREPARING flag. This flag is used
+ * to indicate that there is a thread that is performing a write(9E), but it
+ * is not holding the ccid_mutex because of the operations that it is taking.
+ * Once it has finished, the thread will remove that flag and instead
+ * CCID_IO_F_IN_PROGRESS will be set. If we find that the CCID_IO_F_PREPARING
+ * flag is set, then we will need to wait for it to be removed before
+ * continuing. The fact that there is an outstanding physical I/O will be
+ * dealt with when we clean up state 2.
+ *
+ * To handle the read case, we have a flag on the ccid_minor_t which indicates
+ * that a thread is blocked on a condition variable (cm_read_cv), waiting for
+ * the I/O to complete. The way this gets cleaned up varies a bit on each of
+ * the different cases as each one will trigger a different error to the
+ * thread. In all cases, the condition variable will be signaled. Then,
+ * whenever the thread comes out of the condition variable it will always
+ * check the state to see if it has been woken up because the transaction is
+ * being closed, the ICC has been removed, or the reader is being
+ * disconnected. In all such cases, the thread in read will end up receiving
+ * an error (ECANCELED, ENXIO, and ENODEV respectively).
+ *
+ * If we have hit the case that this needs to be cleaned up, then the
+ * CCID_MINOR_F_READ_WAITING flag will be set on the ccid_minor_t's flags
+ * member (cm_flags). In this case, the broader system must change the
+ * corresponding system state flag for the appropriate condition, signal the
+ * read cv, and then wait on an additional cv in the minor, the
+ * ccid_iowait_cv).
+ *
+ * Cleaning up the poll state is somewhat simpler. If any of the conditions
+ * (A-C) occur, then we must flag POLLERR. In addition if B and C occur, then
+ * we will flag POLLHUP at the same time. This will guarantee that any threads
+ * in poll(9E) are woken up.
+ *
+ * Cleaning up State 2.
+ *
+ * While the user I/O thread is a somewhat straightforward, the kernel
+ * protocol level is a bit more complicated. The core problem is that when a
+ * user issues a logical I/O through an APDU, that may result in a series of
+ * one or more protocol level physical commands. The core crux of the issue
+ * with cleaning up this state is twofold:
+ *
+ * 1. We don't want to block a user thread while I/O is outstanding
+ * 2. We need to take one of several steps to clean up the aforementioned
+ * I/O
+ *
+ * To try and deal with that, there are a number of different things that we
+ * do. The first thing we do is that we clean up the user state based on the
+ * notes in cleaning up in State 1. Importantly we need to _block_ on this
+ * activity.
+ *
+ * Once that is done, we need to proceed to step 2. Since we're doing only
+ * APDU processing, this is as simple as waiting for that command to complete
+ * and/or potentially issue an abort or reset.
+ *
+ * While this is ongoing an additional flag (CCID_SLOT_F_NEED_IO_TEARDOWN)
+ * will be set on the slot to make sure that we know that we can't issue new
+ * I/O or that we can't proceed to the next transaction until this phase is
+ * finished.
+ *
+ * Cleaning up State 3
+ *
+ * When the ICC is removed, this is not dissimilar to the previous states. To
+ * handle this we need to first make sure that state 1 and state 2 are
+ * finished being cleaned up. We will have to _block_ on this from the worker
+ * thread. The problem is that we have certain values such as the operations
+ * vector, the ATR data, etc. that we need to make sure are still valid while
+ * we're in the process of cleaning up state. Once all that is done and the
+ * worker thread proceeds we will consider processing a new ICC insertion.
+ * The one good side is that if the ICC was removed, then it should be simpler
+ * to handle all of the outstanding I/O.
+ *
+ * Cleaning up State 4
+ *
+ * When the reader is removed, then we need to clean up all the prior states.
+ * However, this is somewhat simpler than the other cases, as once this
+ * happens our detach endpoint will be called to clean up all of our
+ * resources. Therefore, before we call detach, we need to explicitly clean up
+ * state 1; however, we then at this time leave all the remaining state to be
+ * cleaned up during detach(9E) as part of normal tear down.
+ */
+
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cmn_err.h>
+#include <sys/sysmacros.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/strsubr.h>
+#include <sys/filio.h>
+
+#define USBDRV_MAJOR_VER 2
+#define USBDRV_MINOR_VER 0
+#include <sys/usb/usba.h>
+#include <sys/usb/usba/usbai_private.h>
+#include <sys/usb/clients/ccid/ccid.h>
+#include <sys/usb/clients/ccid/uccid.h>
+
+#include <atr.h>
+
+/*
+ * Set the amount of parallelism we'll want to have from kernel threads which
+ * are processing CCID requests. This is used to size the number of asynchronous
+ * requests in the pipe policy. A single command can only ever be outstanding to
+ * a single slot. However, multiple slots may potentially be able to be
+ * scheduled in parallel. However, we don't actually support this at all and
+ * we'll only ever issue a single command. This basically covers the ability to
+ * have some other asynchronous operation outstanding if needed.
+ */
+#define CCID_NUM_ASYNC_REQS 2
+
+/*
+ * This is the number of Bulk-IN requests that we will have cached per CCID
+ * device. While many commands will generate a single response, the commands
+ * also have the ability to generate time extensions, which means that we'll
+ * want to be able to schedule another Bulk-IN request immediately. If we run
+ * out, we will attempt to refill said cache and will not fail commands
+ * needlessly.
+ */
+#define CCID_BULK_NALLOCED 16
+
+/*
+ * This is a time in seconds for the bulk-out command to run and be submitted.
+ */
+#define CCID_BULK_OUT_TIMEOUT 5
+#define CCID_BULK_IN_TIMEOUT 5
+
+/*
+ * There are two different Interrupt-IN packets that we might receive. The
+ * first, RDR_to_PC_HardwareError, is a fixed four byte packet. However, the
+ * other one, RDR_to_PC_NotifySlotChange, varies in size as it has two bits per
+ * potential slot plus one byte that's always used. The maximum number of slots
+ * in a device is 256. This means there can be up to 64 bytes worth of data plus
+ * the extra byte, so 65 bytes.
+ */
+#define CCID_INTR_RESPONSE_SIZE 65
+
+/*
+ * Minimum and maximum minor ids. We treat the maximum valid 32-bit minor as
+ * what we can use due to issues in some file systems and the minors that they
+ * can use. We reserved zero as an invalid minor number to make it easier to
+ * tell if things have been initialized or not.
+ */
+#define CCID_MINOR_MIN 1
+#define CCID_MINOR_MAX MAXMIN32
+#define CCID_MINOR_INVALID 0
+
+/*
+ * This value represents the minimum size value that we require in the CCID
+ * class descriptor's dwMaxCCIDMessageLength member. We got to 64 bytes based on
+ * the required size of a bulk transfer packet size. Especially as many CCID
+ * devices are these class of speeds. The specification does require that the
+ * minimum size of the dwMaxCCIDMessageLength member is at least the size of its
+ * bulk endpoint packet size.
+ */
+#define CCID_MIN_MESSAGE_LENGTH 64
+
+/*
+ * Required forward declarations.
+ */
+struct ccid;
+struct ccid_slot;
+struct ccid_minor;
+struct ccid_command;
+
+/*
+ * This structure is used to map between the global set of minor numbers and the
+ * things represented by them.
+ *
+ * We have two different kinds of minor nodes. The first are CCID slots. The
+ * second are cloned opens of those slots. Each of these items has a
+ * ccid_minor_idx_t embedded in them that is used to index them in an AVL tree.
+ * Given that the number of entries that should be present here is unlikely to
+ * be terribly large at any given time, it is hoped that an AVL tree will
+ * suffice for now.
+ */
+typedef struct ccid_minor_idx {
+ id_t cmi_minor;
+ avl_node_t cmi_avl;
+ boolean_t cmi_isslot;
+ union {
+ struct ccid_slot *cmi_slot;
+ struct ccid_minor *cmi_user;
+ } cmi_data;
+} ccid_minor_idx_t;
+
+typedef enum ccid_minor_flags {
+ CCID_MINOR_F_WAITING = 1 << 0,
+ CCID_MINOR_F_HAS_EXCL = 1 << 1,
+ CCID_MINOR_F_TXN_RESET = 1 << 2,
+ CCID_MINOR_F_READ_WAITING = 1 << 3,
+ CCID_MINOR_F_WRITABLE = 1 << 4,
+} ccid_minor_flags_t;
+
+typedef struct ccid_minor {
+ ccid_minor_idx_t cm_idx; /* write-once */
+ cred_t *cm_opener; /* write-once */
+ struct ccid_slot *cm_slot; /* write-once */
+ list_node_t cm_minor_list;
+ list_node_t cm_excl_list;
+ kcondvar_t cm_read_cv;
+ kcondvar_t cm_iowait_cv;
+ kcondvar_t cm_excl_cv;
+ ccid_minor_flags_t cm_flags;
+ struct pollhead cm_pollhead;
+} ccid_minor_t;
+
+typedef enum ccid_slot_flags {
+ CCID_SLOT_F_CHANGED = 1 << 0,
+ CCID_SLOT_F_INTR_GONE = 1 << 1,
+ CCID_SLOT_F_INTR_ADD = 1 << 2,
+ CCID_SLOT_F_PRESENT = 1 << 3,
+ CCID_SLOT_F_ACTIVE = 1 << 4,
+ CCID_SLOT_F_NEED_TXN_RESET = 1 << 5,
+ CCID_SLOT_F_NEED_IO_TEARDOWN = 1 << 6,
+ CCID_SLOT_F_INTR_OVERCURRENT = 1 << 7,
+} ccid_slot_flags_t;
+
+#define CCID_SLOT_F_INTR_MASK (CCID_SLOT_F_CHANGED | CCID_SLOT_F_INTR_GONE | \
+ CCID_SLOT_F_INTR_ADD)
+#define CCID_SLOT_F_WORK_MASK (CCID_SLOT_F_INTR_MASK | \
+ CCID_SLOT_F_NEED_TXN_RESET | CCID_SLOT_F_INTR_OVERCURRENT)
+#define CCID_SLOT_F_NOEXCL_MASK (CCID_SLOT_F_NEED_TXN_RESET | \
+ CCID_SLOT_F_NEED_IO_TEARDOWN)
+
+typedef void (*icc_init_func_t)(struct ccid *, struct ccid_slot *);
+typedef int (*icc_transmit_func_t)(struct ccid *, struct ccid_slot *);
+typedef void (*icc_complete_func_t)(struct ccid *, struct ccid_slot *,
+ struct ccid_command *);
+typedef void (*icc_teardown_func_t)(struct ccid *, struct ccid_slot *, int);
+typedef void (*icc_fini_func_t)(struct ccid *, struct ccid_slot *);
+
+typedef struct ccid_icc {
+ atr_data_t *icc_atr_data;
+ atr_protocol_t icc_protocols;
+ atr_protocol_t icc_cur_protocol;
+ ccid_params_t icc_params;
+ icc_init_func_t icc_init;
+ icc_transmit_func_t icc_tx;
+ icc_complete_func_t icc_complete;
+ icc_teardown_func_t icc_teardown;
+ icc_fini_func_t icc_fini;
+} ccid_icc_t;
+
+/*
+ * Structure used to take care of and map I/O requests and things. This may not
+ * make sense as we develop the T=0 and T=1 code.
+ */
+typedef enum ccid_io_flags {
+ /*
+ * This flag is used during the period that a thread has started calling
+ * into ccid_write(9E), but before it has finished queuing up the write.
+ * This blocks pollout or another thread in write.
+ */
+ CCID_IO_F_PREPARING = 1 << 0,
+ /*
+ * This flag is used once a ccid_write() ICC tx function has
+ * successfully completed. While this is set, the device is not
+ * writable; however, it is legal to call ccid_read() and block. This
+ * flag will remain set until the actual write is done. This indicates
+ * that the transmission protocol has finished.
+ */
+ CCID_IO_F_IN_PROGRESS = 1 << 1,
+ /*
+ * This flag is used to indicate that the logical I/O has completed in
+ * one way or the other and that a reader can consume data. When this
+ * flag is set, then POLLIN | POLLRDNORM should be signaled. Until the
+ * I/O is consumed via ccid_read(), calls to ccid_write() will fail with
+ * EBUSY. When this flag is set, the kernel protocol level should be
+ * idle and it should be safe to tear down.
+ */
+ CCID_IO_F_DONE = 1 << 2,
+} ccid_io_flags_t;
+
+/*
+ * If any of the flags in the POLLOUT group are set, then the device is not
+ * writeable. The same distinction isn't true for POLLIN. We are only readable
+ * if CCID_IO_F_DONE is set. However, you are allowed to call read as soon as
+ * CCID_IO_F_IN_PROGRESS is set.
+ */
+#define CCID_IO_F_POLLOUT_FLAGS (CCID_IO_F_PREPARING | CCID_IO_F_IN_PROGRESS | \
+ CCID_IO_F_DONE)
+#define CCID_IO_F_ALL_FLAGS (CCID_IO_F_PREPARING | CCID_IO_F_IN_PROGRESS | \
+ CCID_IO_F_DONE | CCID_IO_F_ABANDONED)
+
+typedef struct ccid_io {
+ ccid_io_flags_t ci_flags;
+ size_t ci_ilen;
+ uint8_t ci_ibuf[CCID_APDU_LEN_MAX];
+ mblk_t *ci_omp;
+ kcondvar_t ci_cv;
+ struct ccid_command *ci_command;
+ int ci_errno;
+ mblk_t *ci_data;
+} ccid_io_t;
+
+typedef struct ccid_slot {
+ ccid_minor_idx_t cs_idx; /* WO */
+ uint_t cs_slotno; /* WO */
+ struct ccid *cs_ccid; /* WO */
+ ccid_slot_flags_t cs_flags;
+ ccid_class_voltage_t cs_voltage;
+ mblk_t *cs_atr;
+ struct ccid_command *cs_command;
+ ccid_minor_t *cs_excl_minor;
+ list_t cs_excl_waiters;
+ list_t cs_minors;
+ ccid_icc_t cs_icc;
+ ccid_io_t cs_io;
+} ccid_slot_t;
+
+typedef enum ccid_attach_state {
+ CCID_ATTACH_USB_CLIENT = 1 << 0,
+ CCID_ATTACH_MUTEX_INIT = 1 << 1,
+ CCID_ATTACH_TASKQ = 1 << 2,
+ CCID_ATTACH_CMD_LIST = 1 << 3,
+ CCID_ATTACH_OPEN_PIPES = 1 << 4,
+ CCID_ATTACH_SEQ_IDS = 1 << 5,
+ CCID_ATTACH_SLOTS = 1 << 6,
+ CCID_ATTACH_HOTPLUG_CB = 1 << 7,
+ CCID_ATTACH_INTR_ACTIVE = 1 << 8,
+ CCID_ATTACH_MINORS = 1 << 9,
+} ccid_attach_state_t;
+
+typedef enum ccid_flags {
+ CCID_F_HAS_INTR = 1 << 0,
+ CCID_F_NEEDS_PPS = 1 << 1,
+ CCID_F_NEEDS_PARAMS = 1 << 2,
+ CCID_F_NEEDS_DATAFREQ = 1 << 3,
+ CCID_F_DETACHING = 1 << 5,
+ CCID_F_WORKER_REQUESTED = 1 << 6,
+ CCID_F_WORKER_RUNNING = 1 << 7,
+ CCID_F_DISCONNECTED = 1 << 8
+} ccid_flags_t;
+
+#define CCID_F_DEV_GONE_MASK (CCID_F_DETACHING | CCID_F_DISCONNECTED)
+#define CCID_F_WORKER_MASK (CCID_F_WORKER_REQUESTED | \
+ CCID_F_WORKER_RUNNING)
+
+typedef struct ccid_stats {
+ uint64_t cst_intr_errs;
+ uint64_t cst_intr_restart;
+ uint64_t cst_intr_unknown;
+ uint64_t cst_intr_slot_change;
+ uint64_t cst_intr_hwerr;
+ uint64_t cst_intr_inval;
+ uint64_t cst_ndiscover;
+ hrtime_t cst_lastdiscover;
+} ccid_stats_t;
+
+typedef struct ccid {
+ dev_info_t *ccid_dip;
+ kmutex_t ccid_mutex;
+ ccid_attach_state_t ccid_attach;
+ ccid_flags_t ccid_flags;
+ id_space_t *ccid_seqs;
+ ddi_taskq_t *ccid_taskq;
+ usb_client_dev_data_t *ccid_dev_data;
+ ccid_class_descr_t ccid_class; /* WO */
+ usb_ep_xdescr_t ccid_bulkin_xdesc; /* WO */
+ usb_pipe_handle_t ccid_bulkin_pipe; /* WO */
+ usb_ep_xdescr_t ccid_bulkout_xdesc; /* WO */
+ usb_pipe_handle_t ccid_bulkout_pipe; /* WO */
+ usb_ep_xdescr_t ccid_intrin_xdesc; /* WO */
+ usb_pipe_handle_t ccid_intrin_pipe; /* WO */
+ usb_pipe_handle_t ccid_control_pipe; /* WO */
+ uint_t ccid_nslots; /* WO */
+ size_t ccid_bufsize; /* WO */
+ ccid_slot_t *ccid_slots;
+ timeout_id_t ccid_poll_timeout;
+ ccid_stats_t ccid_stats;
+ list_t ccid_command_queue;
+ list_t ccid_complete_queue;
+ usb_bulk_req_t *ccid_bulkin_cache[CCID_BULK_NALLOCED];
+ uint_t ccid_bulkin_alloced;
+ usb_bulk_req_t *ccid_bulkin_dispatched;
+} ccid_t;
+
+/*
+ * Command structure for an individual CCID command that we issue to a
+ * controller. Note that the command caches a copy of some of the data that's
+ * normally inside the CCID header in host-endian fashion.
+ */
+typedef enum ccid_command_state {
+ CCID_COMMAND_ALLOCATED = 0x0,
+ CCID_COMMAND_QUEUED,
+ CCID_COMMAND_DISPATCHED,
+ CCID_COMMAND_REPLYING,
+ CCID_COMMAND_COMPLETE,
+ CCID_COMMAND_TRANSPORT_ERROR,
+ CCID_COMMAND_CCID_ABORTED
+} ccid_command_state_t;
+
+typedef enum ccid_command_flags {
+ CCID_COMMAND_F_USER = 1 << 0,
+} ccid_command_flags_t;
+
+typedef struct ccid_command {
+ list_node_t cc_list_node;
+ kcondvar_t cc_cv;
+ uint8_t cc_mtype;
+ ccid_response_code_t cc_rtype;
+ uint8_t cc_slot;
+ ccid_command_state_t cc_state;
+ ccid_command_flags_t cc_flags;
+ int cc_usb;
+ usb_cr_t cc_usbcr;
+ size_t cc_reqlen;
+ id_t cc_seq;
+ usb_bulk_req_t *cc_ubrp;
+ ccid_t *cc_ccid;
+ hrtime_t cc_queue_time;
+ hrtime_t cc_dispatch_time;
+ hrtime_t cc_dispatch_cb_time;
+ hrtime_t cc_response_time;
+ hrtime_t cc_completion_time;
+ mblk_t *cc_response;
+} ccid_command_t;
+
+/*
+ * ddi_soft_state(9F) pointer. This is used for instances of a CCID controller.
+ */
+static void *ccid_softstate;
+
+/*
+ * This is used to keep track of our minor nodes.
+ */
+static kmutex_t ccid_idxlock;
+static avl_tree_t ccid_idx;
+static id_space_t *ccid_minors;
+
+/*
+ * Required Forwards
+ */
+static void ccid_intr_poll_init(ccid_t *);
+static void ccid_worker_request(ccid_t *);
+static void ccid_command_dispatch(ccid_t *);
+static void ccid_command_free(ccid_command_t *);
+static int ccid_bulkin_schedule(ccid_t *);
+static void ccid_command_bcopy(ccid_command_t *, const void *, size_t);
+
+static int ccid_write_apdu(ccid_t *, ccid_slot_t *);
+static void ccid_complete_apdu(ccid_t *, ccid_slot_t *, ccid_command_t *);
+static void ccid_teardown_apdu(ccid_t *, ccid_slot_t *, int);
+
+
+static int
+ccid_idx_comparator(const void *l, const void *r)
+{
+ const ccid_minor_idx_t *lc = l, *rc = r;
+
+ if (lc->cmi_minor > rc->cmi_minor)
+ return (1);
+ if (lc->cmi_minor < rc->cmi_minor)
+ return (-1);
+ return (0);
+}
+
+static void
+ccid_error(ccid_t *ccid, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (ccid != NULL) {
+ vdev_err(ccid->ccid_dip, CE_WARN, fmt, ap);
+ } else {
+ vcmn_err(CE_WARN, fmt, ap);
+ }
+ va_end(ap);
+}
+
+static void
+ccid_minor_idx_free(ccid_minor_idx_t *idx)
+{
+ ccid_minor_idx_t *ip;
+
+ VERIFY3S(idx->cmi_minor, !=, CCID_MINOR_INVALID);
+ mutex_enter(&ccid_idxlock);
+ ip = avl_find(&ccid_idx, idx, NULL);
+ VERIFY3P(idx, ==, ip);
+ avl_remove(&ccid_idx, idx);
+ id_free(ccid_minors, idx->cmi_minor);
+ idx->cmi_minor = CCID_MINOR_INVALID;
+ mutex_exit(&ccid_idxlock);
+}
+
+static boolean_t
+ccid_minor_idx_alloc(ccid_minor_idx_t *idx, boolean_t sleep)
+{
+ id_t id;
+
+ mutex_enter(&ccid_idxlock);
+ if (sleep) {
+ id = id_alloc(ccid_minors);
+ } else {
+ id = id_alloc_nosleep(ccid_minors);
+ }
+ if (id == -1) {
+ mutex_exit(&ccid_idxlock);
+ return (B_FALSE);
+ }
+ idx->cmi_minor = id;
+ avl_add(&ccid_idx, idx);
+ mutex_exit(&ccid_idxlock);
+
+ return (B_TRUE);
+}
+
+static ccid_minor_idx_t *
+ccid_minor_find(minor_t m)
+{
+ ccid_minor_idx_t i = { 0 };
+ ccid_minor_idx_t *ret;
+
+ i.cmi_minor = m;
+ mutex_enter(&ccid_idxlock);
+ ret = avl_find(&ccid_idx, &i, NULL);
+ mutex_exit(&ccid_idxlock);
+
+ return (ret);
+}
+
+static ccid_minor_idx_t *
+ccid_minor_find_user(minor_t m)
+{
+ ccid_minor_idx_t *idx;
+
+ idx = ccid_minor_find(m);
+ if (idx == NULL) {
+ return (NULL);
+ }
+ VERIFY0(idx->cmi_isslot);
+ return (idx);
+}
+
+static void
+ccid_clear_io(ccid_io_t *io)
+{
+ freemsg(io->ci_data);
+ io->ci_data = NULL;
+ io->ci_errno = 0;
+ io->ci_flags &= ~CCID_IO_F_DONE;
+ io->ci_ilen = 0;
+ bzero(io->ci_ibuf, sizeof (io->ci_ibuf));
+}
+
+/*
+ * Check if the conditions are met to signal the next exclusive holder. For this
+ * to be true, there should be no one holding it. In addition, there must be
+ * someone in the queue waiting. Finally, we want to make sure that the ICC, if
+ * present, is in a state where it could handle these kinds of issues. That
+ * means that we shouldn't have an outstanding I/O question or warm reset
+ * ongoing. However, we must not block this on the condition of an ICC being
+ * present. But, if the reader has been disconnected, don't signal anyone.
+ */
+static void
+ccid_slot_excl_maybe_signal(ccid_slot_t *slot)
+{
+ ccid_minor_t *cmp;
+
+ VERIFY(MUTEX_HELD(&slot->cs_ccid->ccid_mutex));
+
+ if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0)
+ return;
+ if (slot->cs_excl_minor != NULL)
+ return;
+ if ((slot->cs_flags & CCID_SLOT_F_NOEXCL_MASK) != 0)
+ return;
+ cmp = list_head(&slot->cs_excl_waiters);
+ if (cmp == NULL)
+ return;
+ cv_signal(&cmp->cm_excl_cv);
+}
+
+static void
+ccid_slot_excl_rele(ccid_slot_t *slot)
+{
+ ccid_minor_t *cmp;
+ ccid_t *ccid = slot->cs_ccid;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ VERIFY3P(slot->cs_excl_minor, !=, NULL);
+
+ cmp = slot->cs_excl_minor;
+
+ /*
+ * If we have an outstanding command left by the user when they've
+ * closed the slot, we need to clean up this command. We need to call
+ * the protocol specific handler here to determine what to do. If the
+ * command has completed, but the user has never called read, then it
+ * will simply clean it up. Otherwise it will indicate that there is
+ * some amount of external state still ongoing to take care of and clean
+ * up later.
+ */
+ if (slot->cs_icc.icc_teardown != NULL) {
+ slot->cs_icc.icc_teardown(ccid, slot, ECANCELED);
+ }
+
+ /*
+ * There may either be a thread blocked in read or in the process of
+ * preparing a write. In either case, we need to make sure that they're
+ * woken up or finish, before we finish tear down.
+ */
+ while ((cmp->cm_flags & CCID_MINOR_F_READ_WAITING) != 0 ||
+ (slot->cs_io.ci_flags & CCID_IO_F_PREPARING) != 0) {
+ cv_wait(&cmp->cm_iowait_cv, &ccid->ccid_mutex);
+ }
+
+ /*
+ * At this point, we hold the lock and there should be no other threads
+ * that are past the basic sanity checks. So at this point, note that
+ * this minor no longer has exclusive access (causing other read/write
+ * calls to fail) and start the process of cleaning up the outstanding
+ * I/O on the slot. It is OK that at this point the thread may try to
+ * obtain exclusive access again. It will end up blocking on everything
+ * else.
+ */
+ cmp->cm_flags &= ~CCID_MINOR_F_HAS_EXCL;
+ slot->cs_excl_minor = NULL;
+
+ /*
+ * If at this point, we have an I/O that's noted as being done, but no
+ * one blocked in read, then we need to clean that up. The ICC teardown
+ * function is only designed to take care of in-flight I/Os.
+ */
+ if ((slot->cs_io.ci_flags & CCID_IO_F_DONE) != 0)
+ ccid_clear_io(&slot->cs_io);
+
+ /*
+ * Regardless of when we're polling, we need to go through and error
+ * out.
+ */
+ pollwakeup(&cmp->cm_pollhead, POLLERR);
+
+ /*
+ * If we've been asked to reset the card before handing it off, schedule
+ * that. Otherwise, allow the next entry in the queue to get woken up
+ * and given access to the card.
+ */
+ if ((cmp->cm_flags & CCID_MINOR_F_TXN_RESET) != 0) {
+ slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
+ ccid_worker_request(ccid);
+ cmp->cm_flags &= ~CCID_MINOR_F_TXN_RESET;
+ } else {
+ ccid_slot_excl_maybe_signal(slot);
+ }
+}
+
+static int
+ccid_slot_excl_req(ccid_slot_t *slot, ccid_minor_t *cmp, boolean_t nosleep)
+{
+ VERIFY(MUTEX_HELD(&slot->cs_ccid->ccid_mutex));
+
+ if (slot->cs_excl_minor == cmp) {
+ VERIFY((cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) != 0);
+ return (EEXIST);
+ }
+
+ if ((cmp->cm_flags & CCID_MINOR_F_WAITING) != 0) {
+ return (EINPROGRESS);
+ }
+
+ /*
+ * If we were asked to try and fail quickly, do that before the main
+ * loop.
+ */
+ if (nosleep && slot->cs_excl_minor != NULL &&
+ (slot->cs_flags & CCID_SLOT_F_NOEXCL_MASK) == 0) {
+ return (EBUSY);
+ }
+
+ /*
+ * Mark that we're waiting in case we race with another thread trying to
+ * claim exclusive access for this. Insert ourselves on the wait list.
+ * If for some reason we get a signal, then we can't know for certain if
+ * we had a signal / cv race. In such a case, we always wake up the
+ * next person in the queue (potentially spuriously).
+ */
+ cmp->cm_flags |= CCID_MINOR_F_WAITING;
+ list_insert_tail(&slot->cs_excl_waiters, cmp);
+ while (slot->cs_excl_minor != NULL ||
+ (slot->cs_flags & CCID_SLOT_F_NOEXCL_MASK) != 0) {
+ if (cv_wait_sig(&cmp->cm_excl_cv, &slot->cs_ccid->ccid_mutex)
+ == 0) {
+ /*
+ * Remove ourselves from the list and try to signal the
+ * next thread.
+ */
+ list_remove(&slot->cs_excl_waiters, cmp);
+ cmp->cm_flags &= ~CCID_MINOR_F_WAITING;
+ ccid_slot_excl_maybe_signal(slot);
+ return (EINTR);
+ }
+
+ /*
+ * Check if the reader is going away. If so, then we're done
+ * here.
+ */
+ if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
+ list_remove(&slot->cs_excl_waiters, cmp);
+ cmp->cm_flags &= ~CCID_MINOR_F_WAITING;
+ return (ENODEV);
+ }
+ }
+
+ VERIFY0(slot->cs_flags & CCID_SLOT_F_NOEXCL_MASK);
+ list_remove(&slot->cs_excl_waiters, cmp);
+
+ cmp->cm_flags &= ~CCID_MINOR_F_WAITING;
+ cmp->cm_flags |= CCID_MINOR_F_HAS_EXCL;
+ slot->cs_excl_minor = cmp;
+ return (0);
+}
+
+/*
+ * Check whether or not we're in a state that we can signal a POLLIN. To be able
+ * to signal a POLLIN (meaning that we can read) the following must be true:
+ *
+ * o There is a client that has an exclusive hold open
+ * o There is a data which is readable by the client (an I/O is done).
+ *
+ * Unlike with pollout, we don't care about the state of the ICC.
+ */
+static void
+ccid_slot_pollin_signal(ccid_slot_t *slot)
+{
+ ccid_t *ccid = slot->cs_ccid;
+ ccid_minor_t *cmp = slot->cs_excl_minor;
+
+ if (cmp == NULL)
+ return;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ if ((slot->cs_io.ci_flags & CCID_IO_F_DONE) == 0)
+ return;
+
+ pollwakeup(&cmp->cm_pollhead, POLLIN | POLLRDNORM);
+}
+
+/*
+ * Check whether or not we're in a state that we can signal a POLLOUT. To be
+ * able to signal a POLLOUT (meaning that we can write) the following must be
+ * true:
+ *
+ * o There is a minor which has an exclusive hold on the device
+ * o There is no outstanding I/O activity going on, meaning that there is no
+ * operation in progress and any write data has been consumed.
+ * o There is an ICC present
+ * o There is no outstanding I/O cleanup being done, whether a T=1 abort, a
+ * warm reset, or something else.
+ */
+static void
+ccid_slot_pollout_signal(ccid_slot_t *slot)
+{
+ ccid_t *ccid = slot->cs_ccid;
+ ccid_minor_t *cmp = slot->cs_excl_minor;
+
+ if (cmp == NULL)
+ return;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ if ((slot->cs_io.ci_flags & CCID_IO_F_POLLOUT_FLAGS) != 0 ||
+ (slot->cs_flags & CCID_SLOT_F_ACTIVE) == 0 ||
+ (ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0 ||
+ (slot->cs_flags & CCID_SLOT_F_NEED_IO_TEARDOWN) != 0)
+ return;
+
+ pollwakeup(&cmp->cm_pollhead, POLLOUT);
+}
+
+static void
+ccid_slot_io_teardown_done(ccid_slot_t *slot)
+{
+ ccid_t *ccid = slot->cs_ccid;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ slot->cs_flags &= ~CCID_SLOT_F_NEED_IO_TEARDOWN;
+ cv_broadcast(&slot->cs_io.ci_cv);
+
+ ccid_slot_pollout_signal(slot);
+}
+
+/*
+ * This will probably need to change when we start doing TPDU processing.
+ */
+static size_t
+ccid_command_resp_length(ccid_command_t *cc)
+{
+ uint32_t len;
+ const ccid_header_t *cch;
+
+ VERIFY3P(cc, !=, NULL);
+ VERIFY3P(cc->cc_response, !=, NULL);
+
+ /*
+ * Fetch out an arbitrarily aligned LE uint32_t value from the header.
+ */
+ cch = (ccid_header_t *)cc->cc_response->b_rptr;
+ bcopy(&cch->ch_length, &len, sizeof (len));
+ len = LE_32(len);
+ return (len);
+}
+
+static uint8_t
+ccid_command_resp_param2(ccid_command_t *cc)
+{
+ const ccid_header_t *cch;
+
+ VERIFY3P(cc, !=, NULL);
+ VERIFY3P(cc->cc_response, !=, NULL);
+
+ cch = (ccid_header_t *)cc->cc_response->b_rptr;
+
+ return (cch->ch_param2);
+}
+
+/*
+ * Complete a single command. The way that a command completes depends on the
+ * kind of command that occurs. If this command is flagged as a user command,
+ * that implies that it must be handled in a different way from administrative
+ * commands. User commands are placed into the minor to consume via a read(9E).
+ * Non-user commands are placed into a completion queue and must be picked up
+ * via the ccid_command_poll() interface.
+ */
+static void
+ccid_command_complete(ccid_command_t *cc)
+{
+ ccid_t *ccid = cc->cc_ccid;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ cc->cc_completion_time = gethrtime();
+ list_remove(&ccid->ccid_command_queue, cc);
+
+ if (cc->cc_flags & CCID_COMMAND_F_USER) {
+ ccid_slot_t *slot;
+
+ slot = &ccid->ccid_slots[cc->cc_slot];
+ ASSERT(slot->cs_icc.icc_complete != NULL);
+ slot->cs_icc.icc_complete(ccid, slot, cc);
+ } else {
+ list_insert_tail(&ccid->ccid_complete_queue, cc);
+ cv_broadcast(&cc->cc_cv);
+ }
+
+ /*
+ * Finally, we also need to kick off the next command.
+ */
+ ccid_command_dispatch(ccid);
+}
+
+static void
+ccid_command_state_transition(ccid_command_t *cc, ccid_command_state_t state)
+{
+ VERIFY(MUTEX_HELD(&cc->cc_ccid->ccid_mutex));
+
+ cc->cc_state = state;
+ cv_broadcast(&cc->cc_cv);
+}
+
+static void
+ccid_command_transport_error(ccid_command_t *cc, int usb_status, usb_cr_t cr)
+{
+ VERIFY(MUTEX_HELD(&cc->cc_ccid->ccid_mutex));
+
+ ccid_command_state_transition(cc, CCID_COMMAND_TRANSPORT_ERROR);
+ cc->cc_usb = usb_status;
+ cc->cc_usbcr = cr;
+ cc->cc_response = NULL;
+
+ ccid_command_complete(cc);
+}
+
+static void
+ccid_command_status_decode(ccid_command_t *cc,
+ ccid_reply_command_status_t *comp, ccid_reply_icc_status_t *iccp,
+ ccid_command_err_t *errp)
+{
+ ccid_header_t cch;
+ size_t mblen;
+
+ VERIFY3S(cc->cc_state, ==, CCID_COMMAND_COMPLETE);
+ VERIFY3P(cc->cc_response, !=, NULL);
+ mblen = msgsize(cc->cc_response);
+ VERIFY3U(mblen, >=, sizeof (cch));
+
+ bcopy(cc->cc_response->b_rptr, &cch, sizeof (cch));
+ if (comp != NULL) {
+ *comp = CCID_REPLY_STATUS(cch.ch_param0);
+ }
+
+ if (iccp != NULL) {
+ *iccp = CCID_REPLY_ICC(cch.ch_param0);
+ }
+
+ if (errp != NULL) {
+ *errp = cch.ch_param1;
+ }
+}
+
+static void
+ccid_reply_bulk_cb(usb_pipe_handle_t ph, usb_bulk_req_t *ubrp)
+{
+ size_t mlen;
+ ccid_t *ccid;
+ ccid_slot_t *slot;
+ ccid_header_t cch;
+ ccid_command_t *cc;
+
+ boolean_t header_valid = B_FALSE;
+
+ VERIFY(ubrp->bulk_data != NULL);
+ mlen = msgsize(ubrp->bulk_data);
+ ccid = (ccid_t *)ubrp->bulk_client_private;
+ mutex_enter(&ccid->ccid_mutex);
+
+ /*
+ * Before we do anything else, we should mark that this Bulk-IN request
+ * is no longer being dispatched.
+ */
+ VERIFY3P(ubrp, ==, ccid->ccid_bulkin_dispatched);
+ ccid->ccid_bulkin_dispatched = NULL;
+
+ if ((cc = list_head(&ccid->ccid_command_queue)) == NULL) {
+ /*
+ * This is certainly an odd case. This means that we got some
+ * response but there are no entries in the queue. Go ahead and
+ * free this. We're done here.
+ */
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_bulk_req(ubrp);
+ return;
+ }
+
+ slot = &ccid->ccid_slots[cc->cc_slot];
+
+ if (mlen >= sizeof (ccid_header_t)) {
+ bcopy(ubrp->bulk_data->b_rptr, &cch, sizeof (cch));
+ header_valid = B_TRUE;
+ }
+
+ /*
+ * If the current command isn't in the replying state, then something is
+ * clearly wrong and this probably isn't intended for the current
+ * command. That said, if we have enough bytes, let's check the sequence
+ * number as that might be indicative of a bug otherwise.
+ */
+ if (cc->cc_state != CCID_COMMAND_REPLYING) {
+ if (header_valid) {
+ VERIFY3S(cch.ch_seq, !=, cc->cc_seq);
+ }
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_bulk_req(ubrp);
+ return;
+ }
+
+ /*
+ * CCID section 6.2.7 says that if we get a short or zero length packet,
+ * then we need to treat that as though the running command was aborted
+ * for some reason. However, section 3.1.3 talks about sending zero
+ * length packets on general principle. To further complicate things,
+ * we don't have the sequence number.
+ *
+ * If we have an outstanding command still, then we opt to treat the
+ * zero length packet as an abort.
+ */
+ if (!header_valid) {
+ ccid_command_state_transition(cc, CCID_COMMAND_CCID_ABORTED);
+ ccid_command_complete(cc);
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_bulk_req(ubrp);
+ return;
+ }
+
+ /*
+ * If the sequence or slot number don't match the head of the list or
+ * the response type is unexpected for this command then we should be
+ * very suspect of the hardware at this point. At a minimum we should
+ * fail this command and issue a reset.
+ */
+ if (cch.ch_seq != cc->cc_seq ||
+ cch.ch_slot != cc->cc_slot ||
+ cch.ch_mtype != cc->cc_rtype) {
+ ccid_command_state_transition(cc, CCID_COMMAND_CCID_ABORTED);
+ ccid_command_complete(cc);
+ slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
+ ccid_worker_request(ccid);
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_bulk_req(ubrp);
+ return;
+ }
+
+ /*
+ * Check that we have all the bytes that we were told we'd have. If we
+ * don't, simulate this as an aborted command and issue a reset.
+ */
+ if (LE_32(cch.ch_length) + sizeof (ccid_header_t) > mlen) {
+ ccid_command_state_transition(cc, CCID_COMMAND_CCID_ABORTED);
+ ccid_command_complete(cc);
+ slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
+ ccid_worker_request(ccid);
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_bulk_req(ubrp);
+ return;
+ }
+
+ /*
+ * This response is for us. Before we complete the command check to see
+ * what the state of the command is. If the command indicates that more
+ * time has been requested, then we need to schedule a new Bulk-IN
+ * request.
+ */
+ if (CCID_REPLY_STATUS(cch.ch_param0) == CCID_REPLY_STATUS_MORE_TIME) {
+ int ret;
+
+ ret = ccid_bulkin_schedule(ccid);
+ if (ret != USB_SUCCESS) {
+ ccid_command_transport_error(cc, ret, USB_CR_OK);
+ slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
+ ccid_worker_request(ccid);
+ }
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_bulk_req(ubrp);
+ return;
+ }
+
+ /*
+ * Take the message block from the Bulk-IN request and store it on the
+ * command. We want this regardless if it succeeded, failed, or we have
+ * some unexpected status value.
+ */
+ cc->cc_response = ubrp->bulk_data;
+ ubrp->bulk_data = NULL;
+ ccid_command_state_transition(cc, CCID_COMMAND_COMPLETE);
+ ccid_command_complete(cc);
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_bulk_req(ubrp);
+}
+
+static void
+ccid_reply_bulk_exc_cb(usb_pipe_handle_t ph, usb_bulk_req_t *ubrp)
+{
+ ccid_t *ccid;
+ ccid_command_t *cc;
+
+ ccid = (ccid_t *)ubrp->bulk_client_private;
+ mutex_enter(&ccid->ccid_mutex);
+
+ /*
+ * Before we do anything else, we should mark that this Bulk-IN request
+ * is no longer being dispatched.
+ */
+ VERIFY3P(ubrp, ==, ccid->ccid_bulkin_dispatched);
+ ccid->ccid_bulkin_dispatched = NULL;
+
+ /*
+ * While there are many different reasons that the Bulk-IN request could
+ * have failed, each of these are treated as a transport error. If we
+ * have a dispatched command, then we treat this as corresponding to
+ * that command. Otherwise, we drop this.
+ */
+ if ((cc = list_head(&ccid->ccid_command_queue)) != NULL) {
+ if (cc->cc_state == CCID_COMMAND_REPLYING) {
+ ccid_command_transport_error(cc, USB_SUCCESS,
+ ubrp->bulk_completion_reason);
+ }
+ }
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_bulk_req(ubrp);
+}
+
+/*
+ * Fill the Bulk-IN cache. If we do not entirely fill this, that's fine. If
+ * there are no scheduled resources then we'll deal with that when we actually
+ * get there.
+ */
+static void
+ccid_bulkin_cache_refresh(ccid_t *ccid)
+{
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ while (ccid->ccid_bulkin_alloced < CCID_BULK_NALLOCED) {
+ usb_bulk_req_t *ubrp;
+
+ if ((ubrp = usb_alloc_bulk_req(ccid->ccid_dip,
+ ccid->ccid_bufsize, 0)) == NULL)
+ return;
+
+ ubrp->bulk_len = ccid->ccid_bufsize;
+ ubrp->bulk_timeout = CCID_BULK_IN_TIMEOUT;
+ ubrp->bulk_client_private = (usb_opaque_t)ccid;
+ ubrp->bulk_attributes = USB_ATTRS_SHORT_XFER_OK |
+ USB_ATTRS_AUTOCLEARING;
+ ubrp->bulk_cb = ccid_reply_bulk_cb;
+ ubrp->bulk_exc_cb = ccid_reply_bulk_exc_cb;
+
+ ccid->ccid_bulkin_cache[ccid->ccid_bulkin_alloced] = ubrp;
+ ccid->ccid_bulkin_alloced++;
+ }
+
+}
+
+static usb_bulk_req_t *
+ccid_bulkin_cache_get(ccid_t *ccid)
+{
+ usb_bulk_req_t *ubrp;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ if (ccid->ccid_bulkin_alloced == 0) {
+ ccid_bulkin_cache_refresh(ccid);
+ if (ccid->ccid_bulkin_alloced == 0)
+ return (NULL);
+ }
+
+ ccid->ccid_bulkin_alloced--;
+ ubrp = ccid->ccid_bulkin_cache[ccid->ccid_bulkin_alloced];
+ VERIFY3P(ubrp, !=, NULL);
+ ccid->ccid_bulkin_cache[ccid->ccid_bulkin_alloced] = NULL;
+
+ return (ubrp);
+}
+
+/*
+ * Attempt to schedule a Bulk-In request. Note that only one should ever be
+ * scheduled at any time.
+ */
+static int
+ccid_bulkin_schedule(ccid_t *ccid)
+{
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ if (ccid->ccid_bulkin_dispatched == NULL) {
+ usb_bulk_req_t *ubrp;
+ int ret;
+
+ ubrp = ccid_bulkin_cache_get(ccid);
+ if (ubrp == NULL) {
+ return (USB_NO_RESOURCES);
+ }
+
+ if ((ret = usb_pipe_bulk_xfer(ccid->ccid_bulkin_pipe, ubrp,
+ 0)) != USB_SUCCESS) {
+ ccid_error(ccid,
+ "!failed to schedule Bulk-In response: %d", ret);
+ usb_free_bulk_req(ubrp);
+ return (ret);
+ }
+
+ ccid->ccid_bulkin_dispatched = ubrp;
+ }
+
+ return (USB_SUCCESS);
+}
+
+/*
+ * Make sure that the head of the queue has been dispatched. If a dispatch to
+ * the device fails, fail the command and try the next one.
+ */
+static void
+ccid_command_dispatch(ccid_t *ccid)
+{
+ ccid_command_t *cc;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ while ((cc = list_head(&ccid->ccid_command_queue)) != NULL) {
+ int ret;
+
+ if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0)
+ return;
+
+ /*
+ * Head of the queue is already being processed. We're done
+ * here.
+ */
+ if (cc->cc_state > CCID_COMMAND_QUEUED) {
+ return;
+ }
+
+ /*
+ * Mark the command as being dispatched to the device. This
+ * prevents anyone else from getting in and confusing things.
+ */
+ ccid_command_state_transition(cc, CCID_COMMAND_DISPATCHED);
+ cc->cc_dispatch_time = gethrtime();
+
+ /*
+ * Drop the global lock while we schedule the USB I/O.
+ */
+ mutex_exit(&ccid->ccid_mutex);
+
+ ret = usb_pipe_bulk_xfer(ccid->ccid_bulkout_pipe, cc->cc_ubrp,
+ 0);
+ mutex_enter(&ccid->ccid_mutex);
+ if (ret != USB_SUCCESS) {
+ /*
+ * We don't need to free the usb_bulk_req_t here as it
+ * will be taken care of when the command itself is
+ * freed.
+ */
+ ccid_error(ccid, "!Bulk pipe dispatch failed: %d\n",
+ ret);
+ ccid_command_transport_error(cc, ret, USB_CR_OK);
+ }
+ }
+}
+
+static int
+ccid_command_queue(ccid_t *ccid, ccid_command_t *cc)
+{
+ id_t seq;
+ ccid_header_t *cchead;
+
+ seq = id_alloc_nosleep(ccid->ccid_seqs);
+ if (seq == -1)
+ return (ENOMEM);
+ cc->cc_seq = seq;
+ VERIFY3U(seq, <=, UINT8_MAX);
+ cchead = (void *)cc->cc_ubrp->bulk_data->b_rptr;
+ cchead->ch_seq = (uint8_t)seq;
+
+ mutex_enter(&ccid->ccid_mutex);
+ /*
+ * Take a shot at filling up our reply cache while we're submitting this
+ * command.
+ */
+ ccid_bulkin_cache_refresh(ccid);
+ list_insert_tail(&ccid->ccid_command_queue, cc);
+ ccid_command_state_transition(cc, CCID_COMMAND_QUEUED);
+ cc->cc_queue_time = gethrtime();
+ ccid_command_dispatch(ccid);
+ mutex_exit(&ccid->ccid_mutex);
+
+ return (0);
+}
+
+/*
+ * Normal callback for Bulk-Out requests which represents commands issued to the
+ * device.
+ */
+static void
+ccid_dispatch_bulk_cb(usb_pipe_handle_t ph, usb_bulk_req_t *ubrp)
+{
+ int ret;
+ ccid_command_t *cc = (void *)ubrp->bulk_client_private;
+ ccid_t *ccid = cc->cc_ccid;
+
+ mutex_enter(&ccid->ccid_mutex);
+ VERIFY3S(cc->cc_state, ==, CCID_COMMAND_DISPATCHED);
+ ccid_command_state_transition(cc, CCID_COMMAND_REPLYING);
+ cc->cc_dispatch_cb_time = gethrtime();
+
+ /*
+ * Since we have successfully sent the command, give it a Bulk-In
+ * response to reply to us with. If that fails, we'll note a transport
+ * error which will kick off the next command if needed.
+ */
+ ret = ccid_bulkin_schedule(ccid);
+ if (ret != USB_SUCCESS) {
+ ccid_command_transport_error(cc, ret, USB_CR_OK);
+ }
+ mutex_exit(&ccid->ccid_mutex);
+}
+
+/*
+ * Exception callback for the Bulk-Out requests which represent commands issued
+ * to the device.
+ */
+static void
+ccid_dispatch_bulk_exc_cb(usb_pipe_handle_t ph, usb_bulk_req_t *ubrp)
+{
+ ccid_command_t *cc = (void *)ubrp->bulk_client_private;
+ ccid_t *ccid = cc->cc_ccid;
+
+ mutex_enter(&ccid->ccid_mutex);
+ ccid_command_transport_error(cc, USB_SUCCESS,
+ ubrp->bulk_completion_reason);
+ mutex_exit(&ccid->ccid_mutex);
+}
+
+static void
+ccid_command_free(ccid_command_t *cc)
+{
+ VERIFY0(list_link_active(&cc->cc_list_node));
+ VERIFY(cc->cc_state == CCID_COMMAND_ALLOCATED ||
+ cc->cc_state >= CCID_COMMAND_COMPLETE);
+
+ if (cc->cc_response != NULL) {
+ freemsgchain(cc->cc_response);
+ cc->cc_response = NULL;
+ }
+
+ if (cc->cc_ubrp != NULL) {
+ usb_free_bulk_req(cc->cc_ubrp);
+ cc->cc_ubrp = NULL;
+ }
+
+ if (cc->cc_seq != 0) {
+ id_free(cc->cc_ccid->ccid_seqs, cc->cc_seq);
+ cc->cc_seq = 0;
+ }
+
+ cv_destroy(&cc->cc_cv);
+ kmem_free(cc, sizeof (ccid_command_t));
+}
+
+/*
+ * Copy len bytes of data from buf into the allocated message block.
+ */
+static void
+ccid_command_bcopy(ccid_command_t *cc, const void *buf, size_t len)
+{
+ size_t mlen;
+
+ mlen = msgsize(cc->cc_ubrp->bulk_data);
+ VERIFY3U(mlen + len, >=, len);
+ VERIFY3U(mlen + len, >=, mlen);
+ mlen += len;
+ VERIFY3U(mlen, <=, cc->cc_ubrp->bulk_len);
+
+ bcopy(buf, cc->cc_ubrp->bulk_data->b_wptr, len);
+ cc->cc_ubrp->bulk_data->b_wptr += len;
+}
+
+/*
+ * Allocate a command of a specific size and parameters. This will allocate a
+ * USB bulk transfer that the caller will copy data to.
+ */
+static int
+ccid_command_alloc(ccid_t *ccid, ccid_slot_t *slot, boolean_t block,
+ mblk_t *datamp, size_t datasz, uint8_t mtype, uint8_t param0,
+ uint8_t param1, uint8_t param2, ccid_command_t **ccp)
+{
+ size_t allocsz;
+ int kmflag, usbflag;
+ ccid_command_t *cc;
+ ccid_header_t *cchead;
+ ccid_response_code_t rtype;
+
+ switch (mtype) {
+ case CCID_REQUEST_POWER_ON:
+ case CCID_REQUEST_POWER_OFF:
+ case CCID_REQUEST_SLOT_STATUS:
+ case CCID_REQUEST_GET_PARAMS:
+ case CCID_REQUEST_RESET_PARAMS:
+ case CCID_REQUEST_ICC_CLOCK:
+ case CCID_REQUEST_T0APDU:
+ case CCID_REQUEST_MECHANICAL:
+ case CCID_REQEUST_ABORT:
+ if (datasz != 0)
+ return (EINVAL);
+ break;
+ case CCID_REQUEST_TRANSFER_BLOCK:
+ case CCID_REQUEST_ESCAPE:
+ case CCID_REQUEST_SECURE:
+ case CCID_REQUEST_SET_PARAMS:
+ case CCID_REQUEST_DATA_CLOCK:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ switch (mtype) {
+ case CCID_REQUEST_POWER_ON:
+ case CCID_REQUEST_SECURE:
+ case CCID_REQUEST_TRANSFER_BLOCK:
+ rtype = CCID_RESPONSE_DATA_BLOCK;
+ break;
+
+ case CCID_REQUEST_POWER_OFF:
+ case CCID_REQUEST_SLOT_STATUS:
+ case CCID_REQUEST_ICC_CLOCK:
+ case CCID_REQUEST_T0APDU:
+ case CCID_REQUEST_MECHANICAL:
+ case CCID_REQEUST_ABORT:
+ rtype = CCID_RESPONSE_SLOT_STATUS;
+ break;
+
+ case CCID_REQUEST_GET_PARAMS:
+ case CCID_REQUEST_RESET_PARAMS:
+ case CCID_REQUEST_SET_PARAMS:
+ rtype = CCID_RESPONSE_PARAMETERS;
+ break;
+
+ case CCID_REQUEST_ESCAPE:
+ rtype = CCID_RESPONSE_ESCAPE;
+ break;
+
+ case CCID_REQUEST_DATA_CLOCK:
+ rtype = CCID_RESPONSE_DATA_CLOCK;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (block) {
+ kmflag = KM_SLEEP;
+ usbflag = USB_FLAGS_SLEEP;
+ } else {
+ kmflag = KM_NOSLEEP | KM_NORMALPRI;
+ usbflag = 0;
+ }
+
+ if (datasz + sizeof (ccid_header_t) < datasz)
+ return (EINVAL);
+ if (datasz + sizeof (ccid_header_t) > ccid->ccid_bufsize)
+ return (EINVAL);
+
+ cc = kmem_zalloc(sizeof (ccid_command_t), kmflag);
+ if (cc == NULL)
+ return (ENOMEM);
+
+ allocsz = datasz + sizeof (ccid_header_t);
+ if (datamp == NULL) {
+ cc->cc_ubrp = usb_alloc_bulk_req(ccid->ccid_dip, allocsz,
+ usbflag);
+ } else {
+ cc->cc_ubrp = usb_alloc_bulk_req(ccid->ccid_dip, 0, usbflag);
+ }
+ if (cc->cc_ubrp == NULL) {
+ kmem_free(cc, sizeof (ccid_command_t));
+ return (ENOMEM);
+ }
+
+ list_link_init(&cc->cc_list_node);
+ cv_init(&cc->cc_cv, NULL, CV_DRIVER, NULL);
+ cc->cc_mtype = mtype;
+ cc->cc_rtype = rtype;
+ cc->cc_slot = slot->cs_slotno;
+ cc->cc_reqlen = datasz;
+ cc->cc_ccid = ccid;
+ cc->cc_state = CCID_COMMAND_ALLOCATED;
+
+ /*
+ * Fill in bulk request attributes. Note that short transfers out
+ * are not OK.
+ */
+ if (datamp != NULL) {
+ cc->cc_ubrp->bulk_data = datamp;
+ }
+ cc->cc_ubrp->bulk_len = allocsz;
+ cc->cc_ubrp->bulk_timeout = CCID_BULK_OUT_TIMEOUT;
+ cc->cc_ubrp->bulk_client_private = (usb_opaque_t)cc;
+ cc->cc_ubrp->bulk_attributes = USB_ATTRS_AUTOCLEARING;
+ cc->cc_ubrp->bulk_cb = ccid_dispatch_bulk_cb;
+ cc->cc_ubrp->bulk_exc_cb = ccid_dispatch_bulk_exc_cb;
+
+ /*
+ * Fill in the command header. We fill in everything except the sequence
+ * number, which is done by the actual dispatch code.
+ */
+ cchead = (void *)cc->cc_ubrp->bulk_data->b_rptr;
+ cchead->ch_mtype = mtype;
+ cchead->ch_length = LE_32(datasz);
+ cchead->ch_slot = slot->cs_slotno;
+ cchead->ch_seq = 0;
+ cchead->ch_param0 = param0;
+ cchead->ch_param1 = param1;
+ cchead->ch_param2 = param2;
+ cc->cc_ubrp->bulk_data->b_wptr += sizeof (ccid_header_t);
+ *ccp = cc;
+
+ return (0);
+}
+
+/*
+ * The rest of the stack is in charge of timing out commands and potentially
+ * aborting them. At this point in time, there's no specific timeout aspect
+ * here.
+ */
+static void
+ccid_command_poll(ccid_t *ccid, ccid_command_t *cc)
+{
+ VERIFY0(cc->cc_flags & CCID_COMMAND_F_USER);
+
+ mutex_enter(&ccid->ccid_mutex);
+ while ((cc->cc_state < CCID_COMMAND_COMPLETE) &&
+ (ccid->ccid_flags & CCID_F_DEV_GONE_MASK) == 0) {
+ cv_wait(&cc->cc_cv, &ccid->ccid_mutex);
+ }
+
+ /*
+ * Treat this as a consumption and remove it from the completion list.
+ */
+#ifdef DEBUG
+ ccid_command_t *check;
+ for (check = list_head(&ccid->ccid_complete_queue); check != NULL;
+ check = list_next(&ccid->ccid_complete_queue, check)) {
+ if (cc == check)
+ break;
+ }
+ ASSERT3P(check, !=, NULL);
+#endif
+ VERIFY(list_link_active(&cc->cc_list_node));
+ list_remove(&ccid->ccid_complete_queue, cc);
+ mutex_exit(&ccid->ccid_mutex);
+}
+
+static int
+ccid_command_power_off(ccid_t *ccid, ccid_slot_t *cs)
+{
+ int ret;
+ ccid_command_t *cc;
+ ccid_reply_icc_status_t cis;
+ ccid_reply_command_status_t crs;
+
+ if ((ret = ccid_command_alloc(ccid, cs, B_TRUE, NULL, 0,
+ CCID_REQUEST_POWER_OFF, 0, 0, 0, &cc)) != 0) {
+ return (ret);
+ }
+
+ if ((ret = ccid_command_queue(ccid, cc)) != 0) {
+ ccid_command_free(cc);
+ return (ret);
+ }
+
+ ccid_command_poll(ccid, cc);
+
+ if (cc->cc_state != CCID_COMMAND_COMPLETE) {
+ ret = EIO;
+ goto done;
+ }
+
+ ccid_command_status_decode(cc, &crs, &cis, NULL);
+ if (crs == CCID_REPLY_STATUS_FAILED) {
+ if (cis == CCID_REPLY_ICC_MISSING) {
+ ret = ENXIO;
+ } else {
+ ret = EIO;
+ }
+ } else {
+ ret = 0;
+ }
+done:
+ ccid_command_free(cc);
+ return (ret);
+}
+
+static int
+ccid_command_power_on(ccid_t *ccid, ccid_slot_t *cs, ccid_class_voltage_t volt,
+ mblk_t **atrp)
+{
+ int ret;
+ ccid_command_t *cc;
+ ccid_reply_command_status_t crs;
+ ccid_reply_icc_status_t cis;
+ ccid_command_err_t cce;
+
+ if (atrp == NULL)
+ return (EINVAL);
+
+ *atrp = NULL;
+
+ switch (volt) {
+ case CCID_CLASS_VOLT_AUTO:
+ case CCID_CLASS_VOLT_5_0:
+ case CCID_CLASS_VOLT_3_0:
+ case CCID_CLASS_VOLT_1_8:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((ret = ccid_command_alloc(ccid, cs, B_TRUE, NULL, 0,
+ CCID_REQUEST_POWER_ON, volt, 0, 0, &cc)) != 0) {
+ return (ret);
+ }
+
+ if ((ret = ccid_command_queue(ccid, cc)) != 0) {
+ ccid_command_free(cc);
+ return (ret);
+ }
+
+ ccid_command_poll(ccid, cc);
+
+ if (cc->cc_state != CCID_COMMAND_COMPLETE) {
+ ret = EIO;
+ goto done;
+ }
+
+ /*
+ * Look for a few specific errors here:
+ *
+ * - ICC_MUTE via a few potential ways
+ * - Bad voltage
+ */
+ ccid_command_status_decode(cc, &crs, &cis, &cce);
+ if (crs == CCID_REPLY_STATUS_FAILED) {
+ if (cis == CCID_REPLY_ICC_MISSING) {
+ ret = ENXIO;
+ } else if (cis == CCID_REPLY_ICC_INACTIVE &&
+ cce == 7) {
+ /*
+ * This means that byte 7 was invalid. In other words,
+ * that the voltage wasn't correct. See Table 6.1-2
+ * 'Errors' in the CCID r1.1.0 spec.
+ */
+ ret = ENOTSUP;
+ } else {
+ ret = EIO;
+ }
+ } else {
+ size_t len;
+
+ len = ccid_command_resp_length(cc);
+ if (len == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+#ifdef DEBUG
+ /*
+ * This should have already been checked by the response
+ * framework, but sanity check this again.
+ */
+ size_t mlen = msgsize(cc->cc_response);
+ VERIFY3U(mlen, >=, len + sizeof (ccid_header_t));
+#endif
+
+ /*
+ * Munge the message block to have the ATR. We want to make sure
+ * that the write pointer is set to the maximum length that we
+ * got back from the driver (the message block could strictly
+ * speaking be larger, because we got a larger transfer for some
+ * reason).
+ */
+ cc->cc_response->b_rptr += sizeof (ccid_header_t);
+ cc->cc_response->b_wptr = cc->cc_response->b_rptr + len;
+ *atrp = cc->cc_response;
+ cc->cc_response = NULL;
+ ret = 0;
+ }
+
+done:
+ ccid_command_free(cc);
+ return (ret);
+}
+
+static int
+ccid_command_get_parameters(ccid_t *ccid, ccid_slot_t *slot,
+ atr_protocol_t *protp, ccid_params_t *paramsp)
+{
+ int ret;
+ uint8_t prot;
+ size_t mlen;
+ ccid_command_t *cc;
+ ccid_reply_command_status_t crs;
+ ccid_reply_icc_status_t cis;
+ const void *cpbuf;
+
+ if ((ret = ccid_command_alloc(ccid, slot, B_TRUE, NULL, 0,
+ CCID_REQUEST_GET_PARAMS, 0, 0, 0, &cc)) != 0) {
+ return (ret);
+ }
+
+ if ((ret = ccid_command_queue(ccid, cc)) != 0)
+ goto done;
+
+ ccid_command_poll(ccid, cc);
+
+ if (cc->cc_state != CCID_COMMAND_COMPLETE) {
+ ret = EIO;
+ goto done;
+ }
+
+ ccid_command_status_decode(cc, &crs, &cis, NULL);
+ if (crs != CCID_REPLY_STATUS_COMPLETE) {
+ if (cis == CCID_REPLY_ICC_MISSING) {
+ ret = ENXIO;
+ } else {
+ ret = EIO;
+ }
+ goto done;
+ }
+
+ /*
+ * The protocol is in ch_param2 of the header.
+ */
+ prot = ccid_command_resp_param2(cc);
+ mlen = ccid_command_resp_length(cc);
+ cpbuf = cc->cc_response->b_rptr + sizeof (ccid_header_t);
+
+ ret = 0;
+ switch (prot) {
+ case 0:
+ if (mlen < sizeof (ccid_params_t0_t)) {
+ ret = EOVERFLOW;
+ goto done;
+ }
+ *protp = ATR_P_T0;
+ bcopy(cpbuf, &paramsp->ccp_t0, sizeof (ccid_params_t0_t));
+ break;
+ case 1:
+ if (mlen < sizeof (ccid_params_t1_t)) {
+ ret = EOVERFLOW;
+ goto done;
+ }
+ *protp = ATR_P_T1;
+ bcopy(cpbuf, &paramsp->ccp_t1, sizeof (ccid_params_t1_t));
+ break;
+ default:
+ ret = ECHRNG;
+ break;
+ }
+
+done:
+ ccid_command_free(cc);
+ return (ret);
+}
+
+static void
+ccid_hw_error(ccid_t *ccid, ccid_intr_hwerr_t *hwerr)
+{
+ ccid_slot_t *slot;
+
+ /* Make sure the slot number is within range. */
+ if (hwerr->cih_slot >= ccid->ccid_nslots)
+ return;
+
+ slot = &ccid->ccid_slots[hwerr->cih_slot];
+
+ /* The only error condition defined by the spec is overcurrent. */
+ if (hwerr->cih_code != CCID_INTR_HWERR_OVERCURRENT)
+ return;
+
+ /*
+ * The worker thread will take care of this situation.
+ */
+ slot->cs_flags |= CCID_SLOT_F_INTR_OVERCURRENT;
+ ccid_worker_request(ccid);
+}
+
+static void
+ccid_intr_pipe_cb(usb_pipe_handle_t ph, usb_intr_req_t *uirp)
+{
+ mblk_t *mp;
+ size_t msglen, explen;
+ uint_t i;
+ boolean_t change;
+ ccid_intr_hwerr_t ccid_hwerr;
+ ccid_t *ccid = (ccid_t *)uirp->intr_client_private;
+
+ mp = uirp->intr_data;
+ if (mp == NULL)
+ goto done;
+
+ msglen = msgsize(mp);
+ if (msglen == 0)
+ goto done;
+
+ switch (mp->b_rptr[0]) {
+ case CCID_INTR_CODE_SLOT_CHANGE:
+ mutex_enter(&ccid->ccid_mutex);
+ ccid->ccid_stats.cst_intr_slot_change++;
+
+ explen = 1 + ((2 * ccid->ccid_nslots + (NBBY-1)) / NBBY);
+ if (msglen < explen) {
+ ccid->ccid_stats.cst_intr_inval++;
+ mutex_exit(&ccid->ccid_mutex);
+ goto done;
+ }
+
+ change = B_FALSE;
+ for (i = 0; i < ccid->ccid_nslots; i++) {
+ uint_t byte = (i * 2 / NBBY) + 1;
+ uint_t shift = i * 2 % NBBY;
+ uint_t present = 1 << shift;
+ uint_t delta = 2 << shift;
+
+ if (mp->b_rptr[byte] & delta) {
+ ccid_slot_t *slot = &ccid->ccid_slots[i];
+
+ slot->cs_flags &= ~CCID_SLOT_F_INTR_MASK;
+ slot->cs_flags |= CCID_SLOT_F_CHANGED;
+ if (mp->b_rptr[byte] & present) {
+ slot->cs_flags |= CCID_SLOT_F_INTR_ADD;
+ } else {
+ slot->cs_flags |= CCID_SLOT_F_INTR_GONE;
+ }
+ change = B_TRUE;
+ }
+ }
+
+ if (change) {
+ ccid_worker_request(ccid);
+ }
+ mutex_exit(&ccid->ccid_mutex);
+ break;
+ case CCID_INTR_CODE_HW_ERROR:
+ mutex_enter(&ccid->ccid_mutex);
+ ccid->ccid_stats.cst_intr_hwerr++;
+
+ if (msglen < sizeof (ccid_intr_hwerr_t)) {
+ ccid->ccid_stats.cst_intr_inval++;
+ mutex_exit(&ccid->ccid_mutex);
+ goto done;
+ }
+
+ bcopy(mp->b_rptr, &ccid_hwerr, sizeof (ccid_intr_hwerr_t));
+ ccid_hw_error(ccid, &ccid_hwerr);
+
+ mutex_exit(&ccid->ccid_mutex);
+ break;
+ default:
+ mutex_enter(&ccid->ccid_mutex);
+ ccid->ccid_stats.cst_intr_unknown++;
+ mutex_exit(&ccid->ccid_mutex);
+ break;
+ }
+
+done:
+ usb_free_intr_req(uirp);
+}
+
+static void
+ccid_intr_pipe_except_cb(usb_pipe_handle_t ph, usb_intr_req_t *uirp)
+{
+ ccid_t *ccid = (ccid_t *)uirp->intr_client_private;
+
+ ccid->ccid_stats.cst_intr_errs++;
+ switch (uirp->intr_completion_reason) {
+ case USB_CR_PIPE_RESET:
+ case USB_CR_NO_RESOURCES:
+ ccid->ccid_stats.cst_intr_restart++;
+ ccid_intr_poll_init(ccid);
+ break;
+ default:
+ break;
+ }
+ usb_free_intr_req(uirp);
+}
+
+/*
+ * Clean up all the state associated with this slot and its ICC.
+ */
+static void
+ccid_slot_teardown(ccid_t *ccid, ccid_slot_t *slot, boolean_t signal)
+{
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ if (slot->cs_icc.icc_fini != NULL) {
+ slot->cs_icc.icc_fini(ccid, slot);
+ }
+
+ atr_data_reset(slot->cs_icc.icc_atr_data);
+ slot->cs_icc.icc_protocols = ATR_P_NONE;
+ slot->cs_icc.icc_cur_protocol = ATR_P_NONE;
+ slot->cs_icc.icc_init = NULL;
+ slot->cs_icc.icc_tx = NULL;
+ slot->cs_icc.icc_complete = NULL;
+ slot->cs_icc.icc_teardown = NULL;
+ slot->cs_icc.icc_fini = NULL;
+
+ slot->cs_voltage = 0;
+ freemsgchain(slot->cs_atr);
+ slot->cs_atr = NULL;
+
+ if (signal && slot->cs_excl_minor != NULL) {
+ pollwakeup(&slot->cs_excl_minor->cm_pollhead, POLLHUP);
+ }
+}
+
+/*
+ * Wait for teardown of outstanding user I/O.
+ */
+static void
+ccid_slot_io_teardown(ccid_t *ccid, ccid_slot_t *slot)
+{
+ /*
+ * If there is outstanding user I/O, then we need to go ahead and take
+ * care of that. Once this function returns, the user I/O will have been
+ * dealt with; however, before we can tear down things, we need to make
+ * sure that the logical I/O has been completed.
+ */
+ if (slot->cs_icc.icc_teardown != NULL) {
+ slot->cs_icc.icc_teardown(ccid, slot, ENXIO);
+ }
+
+ while ((slot->cs_flags & CCID_SLOT_F_NEED_IO_TEARDOWN) != 0) {
+ cv_wait(&slot->cs_io.ci_cv, &ccid->ccid_mutex);
+ }
+}
+
+/*
+ * The given CCID slot has been inactivated. Clean up.
+ */
+static void
+ccid_slot_inactive(ccid_t *ccid, ccid_slot_t *slot)
+{
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ slot->cs_flags &= ~CCID_SLOT_F_ACTIVE;
+
+ ccid_slot_io_teardown(ccid, slot);
+
+ /*
+ * Now that we've finished completely waiting for the logical I/O to be
+ * torn down, it's safe for us to proceed with the rest of the needed
+ * tear down.
+ */
+ ccid_slot_teardown(ccid, slot, B_TRUE);
+}
+
+/*
+ * The given CCID slot has been removed. Clean up.
+ */
+static void
+ccid_slot_removed(ccid_t *ccid, ccid_slot_t *slot, boolean_t notify)
+{
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ if ((slot->cs_flags & CCID_SLOT_F_PRESENT) == 0) {
+ VERIFY0(slot->cs_flags & CCID_SLOT_F_ACTIVE);
+ return;
+ }
+
+ /*
+ * This slot is gone, mark the flags accordingly.
+ */
+ slot->cs_flags &= ~CCID_SLOT_F_PRESENT;
+
+ ccid_slot_inactive(ccid, slot);
+}
+
+static void
+ccid_slot_setup_functions(ccid_t *ccid, ccid_slot_t *slot)
+{
+ uint_t bits = CCID_CLASS_F_SHORT_APDU_XCHG | CCID_CLASS_F_EXT_APDU_XCHG;
+
+ slot->cs_icc.icc_init = NULL;
+ slot->cs_icc.icc_tx = NULL;
+ slot->cs_icc.icc_complete = NULL;
+ slot->cs_icc.icc_teardown = NULL;
+ slot->cs_icc.icc_fini = NULL;
+
+ switch (ccid->ccid_class.ccd_dwFeatures & bits) {
+ case CCID_CLASS_F_SHORT_APDU_XCHG:
+ case CCID_CLASS_F_EXT_APDU_XCHG:
+ /*
+ * Readers with extended APDU support always also support
+ * short APDUs. We only ever use short APDUs.
+ */
+ slot->cs_icc.icc_tx = ccid_write_apdu;
+ slot->cs_icc.icc_complete = ccid_complete_apdu;
+ slot->cs_icc.icc_teardown = ccid_teardown_apdu;
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * When we don't have a supported tx function, we don't want to end
+ * up blocking attach. It's important we attach so that users can try
+ * and determine information about the ICC and reader.
+ */
+ if (slot->cs_icc.icc_tx == NULL) {
+ ccid_error(ccid, "!CCID does not support I/O transfers to ICC");
+ }
+}
+
+/*
+ * We have an ICC present in a slot. We require that the reader does all
+ * protocol and parameter related initializations for us. Just parse the ATR
+ * for our own use and use GET_PARAMS to query the parameters the reader set
+ * up for us.
+ */
+static boolean_t
+ccid_slot_params_init(ccid_t *ccid, ccid_slot_t *slot, mblk_t *atr)
+{
+ int ret;
+ atr_parsecode_t p;
+ atr_protocol_t prot;
+ atr_data_t *data;
+
+ /*
+ * Use the slot's atr data structure. This is only used when we're in
+ * the worker context, so it should be safe to access in a lockless
+ * fashion.
+ */
+ data = slot->cs_icc.icc_atr_data;
+ atr_data_reset(data);
+ if ((p = atr_parse(atr->b_rptr, msgsize(atr), data)) != ATR_CODE_OK) {
+ ccid_error(ccid, "!failed to parse ATR data from slot %d: %s",
+ slot->cs_slotno, atr_strerror(p));
+ return (B_FALSE);
+ }
+
+ if ((ret = ccid_command_get_parameters(ccid, slot, &prot,
+ &slot->cs_icc.icc_params)) != 0) {
+ ccid_error(ccid, "!failed to get parameters for slot %u: %d",
+ slot->cs_slotno, ret);
+ return (B_FALSE);
+ }
+
+ slot->cs_icc.icc_protocols = atr_supported_protocols(data);
+ slot->cs_icc.icc_cur_protocol = prot;
+
+ if ((ccid->ccid_flags & (CCID_F_NEEDS_PPS | CCID_F_NEEDS_PARAMS |
+ CCID_F_NEEDS_DATAFREQ)) != 0) {
+ ccid_error(ccid, "!CCID reader does not support required "
+ "protocol/parameter setup automation");
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Set up the ICC function parameters and initialize the ICC engine.
+ */
+static boolean_t
+ccid_slot_prot_init(ccid_t *ccid, ccid_slot_t *slot)
+{
+ ccid_slot_setup_functions(ccid, slot);
+
+ if (slot->cs_icc.icc_init != NULL) {
+ slot->cs_icc.icc_init(ccid, slot);
+ }
+
+ return (B_TRUE);
+}
+
+static int
+ccid_slot_power_on(ccid_t *ccid, ccid_slot_t *slot, ccid_class_voltage_t volts,
+ mblk_t **atr)
+{
+ int ret;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ mutex_exit(&ccid->ccid_mutex);
+ if ((ret = ccid_command_power_on(ccid, slot, volts, atr))
+ != 0) {
+ freemsg(*atr);
+
+ /*
+ * If we got ENXIO, then we know that there is no ICC
+ * present. This could happen for a number of reasons.
+ * For example, we could have just started up and no
+ * card was plugged in (we default to assuming that one
+ * is). Also, some readers won't really tell us that
+ * nothing is there until after the power on fails,
+ * hence why we don't bother with doing a status check
+ * and just try to power on.
+ */
+ if (ret == ENXIO) {
+ mutex_enter(&ccid->ccid_mutex);
+ slot->cs_flags &= ~CCID_SLOT_F_PRESENT;
+ return (ret);
+ }
+
+ /*
+ * If we fail to power off the card, check to make sure
+ * it hasn't been removed.
+ */
+ if (ccid_command_power_off(ccid, slot) == ENXIO) {
+ mutex_enter(&ccid->ccid_mutex);
+ slot->cs_flags &= ~CCID_SLOT_F_PRESENT;
+ return (ENXIO);
+ }
+
+ mutex_enter(&ccid->ccid_mutex);
+ return (ret);
+ }
+
+ if (!ccid_slot_params_init(ccid, slot, *atr)) {
+ ccid_error(ccid, "!failed to set slot paramters for ICC");
+ mutex_enter(&ccid->ccid_mutex);
+ return (ENOTSUP);
+ }
+
+ if (!ccid_slot_prot_init(ccid, slot)) {
+ ccid_error(ccid, "!failed to setup protocol for ICC");
+ mutex_enter(&ccid->ccid_mutex);
+ return (ENOTSUP);
+ }
+
+ mutex_enter(&ccid->ccid_mutex);
+ return (0);
+}
+
+static int
+ccid_slot_power_off(ccid_t *ccid, ccid_slot_t *slot)
+{
+ int ret;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ ccid_slot_io_teardown(ccid, slot);
+
+ /*
+ * Now that we've finished completely waiting for the logical I/O to be
+ * torn down, try and power off the ICC.
+ */
+ mutex_exit(&ccid->ccid_mutex);
+ ret = ccid_command_power_off(ccid, slot);
+ mutex_enter(&ccid->ccid_mutex);
+
+ if (ret != 0)
+ return (ret);
+
+ ccid_slot_inactive(ccid, slot);
+
+ return (ret);
+}
+
+static int
+ccid_slot_inserted(ccid_t *ccid, ccid_slot_t *slot)
+{
+ uint_t nvolts = 4;
+ uint_t cvolt = 0;
+ mblk_t *atr = NULL;
+ ccid_class_voltage_t volts[4] = { CCID_CLASS_VOLT_AUTO,
+ CCID_CLASS_VOLT_5_0, CCID_CLASS_VOLT_3_0, CCID_CLASS_VOLT_1_8 };
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
+ return (0);
+ }
+
+ if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) != 0) {
+ return (0);
+ }
+
+ slot->cs_flags |= CCID_SLOT_F_PRESENT;
+
+ /*
+ * Now, we need to activate this ccid device before we can do anything
+ * with it. First, power on the device. There are two hardware features
+ * which may be at play. There may be automatic voltage detection and
+ * automatic activation on insertion. In theory, when either of those
+ * are present, we should always try to use the auto voltage.
+ *
+ * What's less clear in the specification is if the Auto-Voltage
+ * property is present is if we should try manual voltages or not. For
+ * the moment we do.
+ */
+ if ((ccid->ccid_class.ccd_dwFeatures &
+ (CCID_CLASS_F_AUTO_ICC_ACTIVATE | CCID_CLASS_F_AUTO_ICC_VOLTAGE))
+ == 0) {
+ /* Skip auto-voltage */
+ cvolt++;
+ }
+
+ for (; cvolt < nvolts; cvolt++) {
+ int ret;
+
+ if (volts[cvolt] != CCID_CLASS_VOLT_AUTO &&
+ (ccid->ccid_class.ccd_bVoltageSupport & volts[cvolt])
+ == 0) {
+ continue;
+ }
+
+ if ((ret = ccid_slot_power_on(ccid, slot, volts[cvolt], &atr))
+ != 0) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (cvolt >= nvolts) {
+ ccid_error(ccid, "!failed to activate and power on ICC, no "
+ "supported voltages found");
+ goto notsup;
+ }
+
+ slot->cs_voltage = volts[cvolt];
+ slot->cs_atr = atr;
+ slot->cs_flags |= CCID_SLOT_F_ACTIVE;
+
+ ccid_slot_pollout_signal(slot);
+
+ return (0);
+
+notsup:
+ freemsg(atr);
+ ccid_slot_teardown(ccid, slot, B_FALSE);
+ return (ENOTSUP);
+}
+
+static int
+ccid_slot_warm_reset(ccid_t *ccid, ccid_slot_t *slot)
+{
+ int ret;
+ mblk_t *atr;
+ ccid_class_voltage_t voltage;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ ccid_slot_io_teardown(ccid, slot);
+
+ voltage = slot->cs_voltage;
+
+ ccid_slot_teardown(ccid, slot, B_FALSE);
+
+ ret = ccid_slot_power_on(ccid, slot, voltage, &atr);
+ if (ret != 0) {
+ freemsg(atr);
+ return (ret);
+ }
+
+ slot->cs_voltage = voltage;
+ slot->cs_atr = atr;
+
+ return (ret);
+}
+
+static boolean_t
+ccid_slot_reset(ccid_t *ccid, ccid_slot_t *slot)
+{
+ int ret;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ VERIFY(slot->cs_flags & CCID_SLOT_F_NEED_TXN_RESET);
+ VERIFY(ccid->ccid_flags & CCID_F_WORKER_RUNNING);
+
+ if (ccid->ccid_flags & CCID_F_DEV_GONE_MASK)
+ return (B_TRUE);
+
+ /*
+ * Power off the ICC. This will wait for logical I/O if needed.
+ */
+ ret = ccid_slot_power_off(ccid, slot);
+
+ /*
+ * If we failed to power off the ICC because the ICC is removed, then
+ * just return that we failed, so that we can let the next lap clean
+ * things up by noting that the ICC has been removed.
+ */
+ if (ret != 0 && ret == ENXIO) {
+ return (B_FALSE);
+ }
+
+ if (ret != 0) {
+ ccid_error(ccid, "!failed to reset slot %d for next txn: %d; "
+ "taking another lap", slot->cs_slotno, ret);
+ return (B_FALSE);
+ }
+
+ /*
+ * Mimic a slot insertion to power this back on. Don't worry about
+ * success or failure, because as far as we care for resetting it, we've
+ * done our duty once we've powered it off successfully.
+ */
+ (void) ccid_slot_inserted(ccid, slot);
+
+ return (B_TRUE);
+}
+
+/*
+ * We've been asked to perform some amount of work on the various slots that we
+ * have. This may be because the slot needs to be reset due to the completion of
+ * a transaction or it may be because an ICC inside of the slot has been
+ * removed.
+ */
+static void
+ccid_worker(void *arg)
+{
+ uint_t i;
+ ccid_t *ccid = arg;
+
+ mutex_enter(&ccid->ccid_mutex);
+ ccid->ccid_stats.cst_ndiscover++;
+ ccid->ccid_stats.cst_lastdiscover = gethrtime();
+ ccid->ccid_flags |= CCID_F_WORKER_RUNNING;
+ ccid->ccid_flags &= ~CCID_F_WORKER_REQUESTED;
+
+ for (i = 0; i < ccid->ccid_nslots; i++) {
+ ccid_slot_t *slot = &ccid->ccid_slots[i];
+ uint_t flags;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
+ ccid->ccid_flags &= ~CCID_F_WORKER_MASK;
+ mutex_exit(&ccid->ccid_mutex);
+ return;
+ }
+
+ /*
+ * Snapshot the flags before we start processing the worker. At
+ * this time we clear out all of the change flags as we'll be
+ * operating on the device. We do not clear the
+ * CCID_SLOT_F_NEED_TXN_RESET flag, as we want to make sure that
+ * this is maintained until we're done here.
+ */
+ flags = slot->cs_flags & CCID_SLOT_F_WORK_MASK;
+ slot->cs_flags &= ~CCID_SLOT_F_INTR_MASK;
+
+ if ((flags & CCID_SLOT_F_INTR_OVERCURRENT) != 0) {
+ ccid_slot_inactive(ccid, slot);
+ }
+
+ if ((flags & CCID_SLOT_F_CHANGED) != 0) {
+ if (flags & CCID_SLOT_F_INTR_GONE) {
+ ccid_slot_removed(ccid, slot, B_TRUE);
+ } else {
+ (void) ccid_slot_inserted(ccid, slot);
+ if ((slot->cs_flags & CCID_SLOT_F_ACTIVE)
+ != 0) {
+ ccid_slot_excl_maybe_signal(slot);
+ }
+ }
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ }
+
+ if ((flags & CCID_SLOT_F_NEED_TXN_RESET) != 0) {
+ /*
+ * If the CCID_SLOT_F_PRESENT flag is set, then we
+ * should attempt to power off and power on the ICC in
+ * an attempt to reset it. If this fails, trigger
+ * another worker that needs to operate.
+ */
+ if ((slot->cs_flags & CCID_SLOT_F_PRESENT) != 0) {
+ if (!ccid_slot_reset(ccid, slot)) {
+ ccid_worker_request(ccid);
+ continue;
+ }
+ }
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ slot->cs_flags &= ~CCID_SLOT_F_NEED_TXN_RESET;
+ /*
+ * Try to signal the next thread waiting for exclusive
+ * access.
+ */
+ ccid_slot_excl_maybe_signal(slot);
+ }
+ }
+
+ /*
+ * If we have a request to operate again, delay before we consider this,
+ * to make sure we don't do too much work ourselves.
+ */
+ if ((ccid->ccid_flags & CCID_F_WORKER_REQUESTED) != 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ delay(drv_usectohz(1000) * 10);
+ mutex_enter(&ccid->ccid_mutex);
+ }
+
+ ccid->ccid_flags &= ~CCID_F_WORKER_RUNNING;
+ if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
+ ccid->ccid_flags &= ~CCID_F_WORKER_REQUESTED;
+ mutex_exit(&ccid->ccid_mutex);
+ return;
+ }
+
+ if ((ccid->ccid_flags & CCID_F_WORKER_REQUESTED) != 0) {
+ (void) ddi_taskq_dispatch(ccid->ccid_taskq, ccid_worker, ccid,
+ DDI_SLEEP);
+ }
+ mutex_exit(&ccid->ccid_mutex);
+}
+
+static void
+ccid_worker_request(ccid_t *ccid)
+{
+ boolean_t run;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
+ return;
+ }
+
+ run = (ccid->ccid_flags & CCID_F_WORKER_MASK) == 0;
+ ccid->ccid_flags |= CCID_F_WORKER_REQUESTED;
+ if (run) {
+ mutex_exit(&ccid->ccid_mutex);
+ (void) ddi_taskq_dispatch(ccid->ccid_taskq, ccid_worker, ccid,
+ DDI_SLEEP);
+ mutex_enter(&ccid->ccid_mutex);
+ }
+}
+
+static void
+ccid_intr_restart_timeout(void *arg)
+{
+ ccid_t *ccid = arg;
+
+ mutex_enter(&ccid->ccid_mutex);
+ if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
+ ccid->ccid_poll_timeout = NULL;
+ mutex_exit(&ccid->ccid_mutex);
+ }
+ mutex_exit(&ccid->ccid_mutex);
+
+ ccid_intr_poll_init(ccid);
+}
+
+/*
+ * Search for the current class descriptor from the configuration cloud and
+ * parse it for our use. We do this by first finding the current interface
+ * descriptor and expecting it to be one of the next descriptors.
+ */
+static boolean_t
+ccid_parse_class_desc(ccid_t *ccid)
+{
+ uint_t i;
+ size_t len, tlen;
+ usb_client_dev_data_t *dp;
+ usb_alt_if_data_t *alt;
+
+ /*
+ * Establish the target length we're looking for from usb_parse_data().
+ * Note that we cannot use the sizeof (ccid_class_descr_t) for this
+ * because that function does not know how to account for the padding at
+ * the end of the target structure (which is reasonble). So we manually
+ * figure out the number of bytes it should in theory write.
+ */
+ tlen = offsetof(ccid_class_descr_t, ccd_bMaxCCIDBusySlots) +
+ sizeof (ccid->ccid_class.ccd_bMaxCCIDBusySlots);
+ dp = ccid->ccid_dev_data;
+ alt = &dp->dev_curr_cfg->cfg_if[dp->dev_curr_if].if_alt[0];
+ for (i = 0; i < alt->altif_n_cvs; i++) {
+ usb_cvs_data_t *cvs = &alt->altif_cvs[i];
+ if (cvs->cvs_buf == NULL)
+ continue;
+ if (cvs->cvs_buf_len != CCID_DESCR_LENGTH)
+ continue;
+ if (cvs->cvs_buf[1] != CCID_DESCR_TYPE)
+ continue;
+ if ((len = usb_parse_data("ccscc3lcllc5lccscc", cvs->cvs_buf,
+ cvs->cvs_buf_len, &ccid->ccid_class,
+ sizeof (ccid->ccid_class))) >= tlen) {
+ return (B_TRUE);
+ }
+ ccid_error(ccid, "!failed to parse CCID class descriptor from "
+ "cvs %u, expected %lu bytes, received %lu", i, tlen, len);
+ }
+
+ ccid_error(ccid, "!failed to find matching CCID class descriptor");
+ return (B_FALSE);
+}
+
+/*
+ * Verify whether or not we can support this CCID reader.
+ */
+static boolean_t
+ccid_supported(ccid_t *ccid)
+{
+ usb_client_dev_data_t *dp;
+ usb_alt_if_data_t *alt;
+ ccid_class_features_t feat;
+ uint_t bits;
+ uint16_t ver = ccid->ccid_class.ccd_bcdCCID;
+
+ if (CCID_VERSION_MAJOR(ver) != CCID_VERSION_ONE) {
+ ccid_error(ccid, "!refusing to attach to CCID with unsupported "
+ "version %x.%2x", CCID_VERSION_MAJOR(ver),
+ CCID_VERSION_MINOR(ver));
+ return (B_FALSE);
+ }
+
+ /*
+ * Check the number of endpoints. This should have either two or three.
+ * If three, that means we should expect an interrupt-IN endpoint.
+ * Otherwise, we shouldn't. Any other value indicates something weird
+ * that we should ignore.
+ */
+ dp = ccid->ccid_dev_data;
+ alt = &dp->dev_curr_cfg->cfg_if[dp->dev_curr_if].if_alt[0];
+ switch (alt->altif_descr.bNumEndpoints) {
+ case 2:
+ ccid->ccid_flags &= ~CCID_F_HAS_INTR;
+ break;
+ case 3:
+ ccid->ccid_flags |= CCID_F_HAS_INTR;
+ break;
+ default:
+ ccid_error(ccid, "!refusing to attach to CCID with unsupported "
+ "number of endpoints: %d", alt->altif_descr.bNumEndpoints);
+ return (B_FALSE);
+ }
+
+ /*
+ * Try and determine the appropriate buffer size. This can be a little
+ * tricky. The class descriptor tells us the maximum size that the
+ * reader accepts. While it may be tempting to try and use a larger
+ * value such as the maximum size, the readers really don't like
+ * receiving bulk transfers that large. However, there are also reports
+ * of readers that will overwrite to a fixed minimum size. Until we see
+ * such a thing in the wild there's probably no point in trying to deal
+ * with it here.
+ */
+ ccid->ccid_bufsize = ccid->ccid_class.ccd_dwMaxCCIDMessageLength;
+ if (ccid->ccid_bufsize < CCID_MIN_MESSAGE_LENGTH) {
+ ccid_error(ccid, "!CCID reader maximum CCID message length (%u)"
+ " is less than minimum packet length (%u)",
+ ccid->ccid_bufsize, CCID_MIN_MESSAGE_LENGTH);
+ return (B_FALSE);
+ }
+
+ /*
+ * At this time, we do not require that the system have automatic ICC
+ * activation or automatic ICC voltage. These are handled automatically
+ * by the system.
+ */
+ feat = ccid->ccid_class.ccd_dwFeatures;
+
+ /*
+ * Check the number of data rates that are supported by the reader. If
+ * the reader has a non-zero value and we don't support automatic
+ * negotiation then warn about that.
+ */
+ if (ccid->ccid_class.ccd_bNumDataRatesSupported != 0 &&
+ (feat & CCID_CLASS_F_AUTO_BAUD) == 0) {
+ ccid_error(ccid, "!CCID reader only supports fixed clock rates,"
+ " data will be limited to default values");
+ }
+
+ /*
+ * Check which automatic features the reader provides and which features
+ * it does not. Missing features will require additional work before a
+ * card can be activated. Note, this also applies to APDU based readers
+ * which may need to have various aspects of the device negotiated.
+ */
+
+ /*
+ * The footnote for these two bits in CCID r1.1.0 indicates that
+ * when neither are missing we have to do the PPS negotiation
+ * ourselves.
+ */
+ bits = CCID_CLASS_F_AUTO_PARAM_NEG | CCID_CLASS_F_AUTO_PPS;
+ if ((feat & bits) == 0) {
+ ccid->ccid_flags |= CCID_F_NEEDS_PPS;
+ }
+
+ if ((feat & CCID_CLASS_F_AUTO_PARAM_NEG) == 0) {
+ ccid->ccid_flags |= CCID_F_NEEDS_PARAMS;
+ }
+
+ bits = CCID_CLASS_F_AUTO_BAUD | CCID_CLASS_F_AUTO_ICC_CLOCK;
+ if ((feat & bits) != bits) {
+ ccid->ccid_flags |= CCID_F_NEEDS_DATAFREQ;
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+ccid_open_pipes(ccid_t *ccid)
+{
+ int ret;
+ usb_ep_data_t *ep;
+ usb_client_dev_data_t *data;
+ usb_pipe_policy_t policy;
+
+ data = ccid->ccid_dev_data;
+
+ /*
+ * First fill all the descriptors.
+ */
+ ep = usb_lookup_ep_data(ccid->ccid_dip, data, data->dev_curr_if, 0, 0,
+ USB_EP_ATTR_BULK, USB_EP_DIR_IN);
+ if (ep == NULL) {
+ ccid_error(ccid, "!failed to find CCID Bulk-IN endpoint");
+ return (B_FALSE);
+ }
+
+ if ((ret = usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
+ ccid->ccid_dip, ep, &ccid->ccid_bulkin_xdesc)) != USB_SUCCESS) {
+ ccid_error(ccid, "!failed to fill Bulk-IN xdescr: %d", ret);
+ return (B_FALSE);
+ }
+
+ ep = usb_lookup_ep_data(ccid->ccid_dip, data, data->dev_curr_if, 0, 0,
+ USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
+ if (ep == NULL) {
+ ccid_error(ccid, "!failed to find CCID Bulk-OUT endpoint");
+ return (B_FALSE);
+ }
+
+ if ((ret = usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
+ ccid->ccid_dip, ep, &ccid->ccid_bulkout_xdesc)) != USB_SUCCESS) {
+ ccid_error(ccid, "!failed to fill Bulk-OUT xdescr: %d", ret);
+ return (B_FALSE);
+ }
+
+ if (ccid->ccid_flags & CCID_F_HAS_INTR) {
+ ep = usb_lookup_ep_data(ccid->ccid_dip, data, data->dev_curr_if,
+ 0, 0, USB_EP_ATTR_INTR, USB_EP_DIR_IN);
+ if (ep == NULL) {
+ ccid_error(ccid, "!failed to find CCID Intr-IN "
+ "endpoint");
+ return (B_FALSE);
+ }
+
+ if ((ret = usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
+ ccid->ccid_dip, ep, &ccid->ccid_intrin_xdesc)) !=
+ USB_SUCCESS) {
+ ccid_error(ccid, "!failed to fill Intr-OUT xdescr: %d",
+ ret);
+ return (B_FALSE);
+ }
+ }
+
+ /*
+ * Now open up the pipes.
+ */
+ bzero(&policy, sizeof (policy));
+ policy.pp_max_async_reqs = CCID_NUM_ASYNC_REQS;
+
+ if ((ret = usb_pipe_xopen(ccid->ccid_dip, &ccid->ccid_bulkin_xdesc,
+ &policy, USB_FLAGS_SLEEP, &ccid->ccid_bulkin_pipe)) !=
+ USB_SUCCESS) {
+ ccid_error(ccid, "!failed to open Bulk-IN pipe: %d\n", ret);
+ return (B_FALSE);
+ }
+
+ if ((ret = usb_pipe_xopen(ccid->ccid_dip, &ccid->ccid_bulkout_xdesc,
+ &policy, USB_FLAGS_SLEEP, &ccid->ccid_bulkout_pipe)) !=
+ USB_SUCCESS) {
+ ccid_error(ccid, "!failed to open Bulk-OUT pipe: %d\n", ret);
+ usb_pipe_close(ccid->ccid_dip, ccid->ccid_bulkin_pipe,
+ USB_FLAGS_SLEEP, NULL, NULL);
+ ccid->ccid_bulkin_pipe = NULL;
+ return (B_FALSE);
+ }
+
+ if (ccid->ccid_flags & CCID_F_HAS_INTR) {
+ if ((ret = usb_pipe_xopen(ccid->ccid_dip,
+ &ccid->ccid_intrin_xdesc, &policy, USB_FLAGS_SLEEP,
+ &ccid->ccid_intrin_pipe)) != USB_SUCCESS) {
+ ccid_error(ccid, "!failed to open Intr-IN pipe: %d\n",
+ ret);
+ usb_pipe_close(ccid->ccid_dip, ccid->ccid_bulkin_pipe,
+ USB_FLAGS_SLEEP, NULL, NULL);
+ ccid->ccid_bulkin_pipe = NULL;
+ usb_pipe_close(ccid->ccid_dip, ccid->ccid_bulkout_pipe,
+ USB_FLAGS_SLEEP, NULL, NULL);
+ ccid->ccid_bulkout_pipe = NULL;
+ return (B_FALSE);
+ }
+ }
+
+ ccid->ccid_control_pipe = data->dev_default_ph;
+ return (B_TRUE);
+}
+
+static void
+ccid_slots_fini(ccid_t *ccid)
+{
+ uint_t i;
+
+ for (i = 0; i < ccid->ccid_nslots; i++) {
+ VERIFY3U(ccid->ccid_slots[i].cs_slotno, ==, i);
+
+ if (ccid->ccid_slots[i].cs_command != NULL) {
+ ccid_command_free(ccid->ccid_slots[i].cs_command);
+ ccid->ccid_slots[i].cs_command = NULL;
+ }
+
+ cv_destroy(&ccid->ccid_slots[i].cs_io.ci_cv);
+ freemsgchain(ccid->ccid_slots[i].cs_atr);
+ atr_data_free(ccid->ccid_slots[i].cs_icc.icc_atr_data);
+ list_destroy(&ccid->ccid_slots[i].cs_minors);
+ list_destroy(&ccid->ccid_slots[i].cs_excl_waiters);
+ }
+
+ ddi_remove_minor_node(ccid->ccid_dip, NULL);
+ kmem_free(ccid->ccid_slots, sizeof (ccid_slot_t) * ccid->ccid_nslots);
+ ccid->ccid_nslots = 0;
+ ccid->ccid_slots = NULL;
+}
+
+static boolean_t
+ccid_slots_init(ccid_t *ccid)
+{
+ uint_t i;
+
+ /*
+ * The class descriptor has the maximum index that one can index into.
+ * We therefore have to add one to determine the actual number of slots
+ * that exist.
+ */
+ ccid->ccid_nslots = ccid->ccid_class.ccd_bMaxSlotIndex + 1;
+ ccid->ccid_slots = kmem_zalloc(sizeof (ccid_slot_t) * ccid->ccid_nslots,
+ KM_SLEEP);
+ for (i = 0; i < ccid->ccid_nslots; i++) {
+ ccid_slot_t *slot = &ccid->ccid_slots[i];
+
+ /*
+ * We initialize every possible slot as having changed to make
+ * sure that we have a chance to discover it. See the slot
+ * detection section in the big theory statement for more info.
+ */
+ slot->cs_flags |= CCID_SLOT_F_CHANGED;
+ slot->cs_slotno = i;
+ slot->cs_ccid = ccid;
+ slot->cs_icc.icc_atr_data = atr_data_alloc();
+ slot->cs_idx.cmi_minor = CCID_MINOR_INVALID;
+ slot->cs_idx.cmi_isslot = B_TRUE;
+ slot->cs_idx.cmi_data.cmi_slot = slot;
+ cv_init(&slot->cs_io.ci_cv, NULL, CV_DRIVER, NULL);
+ list_create(&slot->cs_minors, sizeof (ccid_minor_t),
+ offsetof(ccid_minor_t, cm_minor_list));
+ list_create(&slot->cs_excl_waiters, sizeof (ccid_minor_t),
+ offsetof(ccid_minor_t, cm_excl_list));
+ }
+
+ return (B_TRUE);
+}
+
+static void
+ccid_minors_fini(ccid_t *ccid)
+{
+ uint_t i;
+
+ ddi_remove_minor_node(ccid->ccid_dip, NULL);
+ for (i = 0; i < ccid->ccid_nslots; i++) {
+ if (ccid->ccid_slots[i].cs_idx.cmi_minor == CCID_MINOR_INVALID)
+ continue;
+ ccid_minor_idx_free(&ccid->ccid_slots[i].cs_idx);
+ }
+}
+
+static boolean_t
+ccid_minors_init(ccid_t *ccid)
+{
+ uint_t i;
+
+ for (i = 0; i < ccid->ccid_nslots; i++) {
+ char buf[32];
+
+ (void) ccid_minor_idx_alloc(&ccid->ccid_slots[i].cs_idx,
+ B_TRUE);
+
+ (void) snprintf(buf, sizeof (buf), "slot%u", i);
+ if (ddi_create_minor_node(ccid->ccid_dip, buf, S_IFCHR,
+ ccid->ccid_slots[i].cs_idx.cmi_minor,
+ DDI_NT_CCID_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
+ ccid_minors_fini(ccid);
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+static void
+ccid_intr_poll_fini(ccid_t *ccid)
+{
+ if (ccid->ccid_flags & CCID_F_HAS_INTR) {
+ timeout_id_t tid;
+
+ mutex_enter(&ccid->ccid_mutex);
+ tid = ccid->ccid_poll_timeout;
+ ccid->ccid_poll_timeout = NULL;
+ mutex_exit(&ccid->ccid_mutex);
+ (void) untimeout(tid);
+ usb_pipe_stop_intr_polling(ccid->ccid_intrin_pipe,
+ USB_FLAGS_SLEEP);
+ } else {
+ VERIFY3P(ccid->ccid_intrin_pipe, ==, NULL);
+ }
+}
+
+static void
+ccid_intr_poll_init(ccid_t *ccid)
+{
+ int ret;
+ usb_intr_req_t *uirp;
+
+ uirp = usb_alloc_intr_req(ccid->ccid_dip, 0, USB_FLAGS_SLEEP);
+ uirp->intr_client_private = (usb_opaque_t)ccid;
+ uirp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
+ USB_ATTRS_AUTOCLEARING;
+ uirp->intr_len = CCID_INTR_RESPONSE_SIZE;
+ uirp->intr_cb = ccid_intr_pipe_cb;
+ uirp->intr_exc_cb = ccid_intr_pipe_except_cb;
+
+ mutex_enter(&ccid->ccid_mutex);
+ if (ccid->ccid_flags & CCID_F_DEV_GONE_MASK) {
+ mutex_exit(&ccid->ccid_mutex);
+ usb_free_intr_req(uirp);
+ return;
+ }
+
+ if ((ret = usb_pipe_intr_xfer(ccid->ccid_intrin_pipe, uirp,
+ USB_FLAGS_SLEEP)) != USB_SUCCESS) {
+ ccid_error(ccid, "!failed to start polling on CCID Intr-IN "
+ "pipe: %d", ret);
+ ccid->ccid_poll_timeout = timeout(ccid_intr_restart_timeout,
+ ccid, drv_usectohz(1000000));
+ usb_free_intr_req(uirp);
+ }
+ mutex_exit(&ccid->ccid_mutex);
+}
+
+static void
+ccid_cleanup_bulkin(ccid_t *ccid)
+{
+ uint_t i;
+
+ VERIFY3P(ccid->ccid_bulkin_dispatched, ==, NULL);
+ for (i = 0; i < ccid->ccid_bulkin_alloced; i++) {
+ VERIFY3P(ccid->ccid_bulkin_cache[i], !=, NULL);
+ usb_free_bulk_req(ccid->ccid_bulkin_cache[i]);
+ ccid->ccid_bulkin_cache[i] = NULL;
+ }
+
+#ifdef DEBUG
+ for (i = 0; i < CCID_BULK_NALLOCED; i++) {
+ VERIFY3P(ccid->ccid_bulkin_cache[i], ==, NULL);
+ }
+#endif
+ ccid->ccid_bulkin_alloced = 0;
+}
+
+static int
+ccid_disconnect_cb(dev_info_t *dip)
+{
+ int inst;
+ ccid_t *ccid;
+ uint_t i;
+
+ if (dip == NULL)
+ goto done;
+
+ inst = ddi_get_instance(dip);
+ ccid = ddi_get_soft_state(ccid_softstate, inst);
+ if (ccid == NULL)
+ goto done;
+ VERIFY3P(dip, ==, ccid->ccid_dip);
+
+ mutex_enter(&ccid->ccid_mutex);
+ /*
+ * First, set the disconnected flag. This will make sure that anyone
+ * that tries to make additional operations will be kicked out. This
+ * flag is checked by detach and by users.
+ */
+ ccid->ccid_flags |= CCID_F_DISCONNECTED;
+
+ /*
+ * Now, go through any threads that are blocked on a minor for exclusive
+ * access. They should be woken up and they'll fail due to the fact that
+ * we've set the disconnected flag above.
+ */
+ for (i = 0; i < ccid->ccid_nslots; i++) {
+ ccid_minor_t *cmp;
+ ccid_slot_t *slot = &ccid->ccid_slots[i];
+
+ for (cmp = list_head(&slot->cs_excl_waiters); cmp != NULL;
+ cmp = list_next(&slot->cs_excl_waiters, cmp)) {
+ cv_signal(&cmp->cm_excl_cv);
+ }
+ }
+
+ /*
+ * Finally, we need to basically wake up anyone blocked in read and make
+ * sure that they don't wait there forever and make sure that anyone
+ * polling gets a POLLHUP. We can't really distinguish between this and
+ * an ICC being removed. It will be discovered when someone tries to do
+ * an operation and they receive an ENODEV. We only need to do this on
+ * minors that have exclusive access. Don't worry about them finishing
+ * up, this'll be done as part of detach.
+ */
+ for (i = 0; i < ccid->ccid_nslots; i++) {
+ ccid_slot_t *slot = &ccid->ccid_slots[i];
+ if (slot->cs_excl_minor == NULL)
+ continue;
+
+ pollwakeup(&slot->cs_excl_minor->cm_pollhead,
+ POLLHUP | POLLERR);
+ cv_signal(&slot->cs_excl_minor->cm_read_cv);
+ }
+
+ /*
+ * If there are outstanding commands, they will ultimately be cleaned
+ * up as the USB commands themselves time out. We will get notified
+ * through the various bulk xfer exception callbacks, which will induce
+ * the cleanup through ccid_command_transport_error(). This will also
+ * take care of commands waiting for I/O teardown.
+ */
+ mutex_exit(&ccid->ccid_mutex);
+
+done:
+ return (USB_SUCCESS);
+}
+
+static usb_event_t ccid_usb_events = {
+ ccid_disconnect_cb,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void
+ccid_cleanup(dev_info_t *dip)
+{
+ int inst;
+ ccid_t *ccid;
+
+ if (dip == NULL)
+ return;
+
+ inst = ddi_get_instance(dip);
+ ccid = ddi_get_soft_state(ccid_softstate, inst);
+ if (ccid == NULL)
+ return;
+ VERIFY3P(dip, ==, ccid->ccid_dip);
+
+ /*
+ * Make sure we set the detaching flag so anything running in the
+ * background knows to stop.
+ */
+ mutex_enter(&ccid->ccid_mutex);
+ ccid->ccid_flags |= CCID_F_DETACHING;
+ mutex_exit(&ccid->ccid_mutex);
+
+ if ((ccid->ccid_attach & CCID_ATTACH_MINORS) != 0) {
+ ccid_minors_fini(ccid);
+ ccid->ccid_attach &= ~CCID_ATTACH_MINORS;
+ }
+
+ if ((ccid->ccid_attach & CCID_ATTACH_INTR_ACTIVE) != 0) {
+ ccid_intr_poll_fini(ccid);
+ ccid->ccid_attach &= ~CCID_ATTACH_INTR_ACTIVE;
+ }
+
+ /*
+ * At this point, we have shut down the interrupt pipe, the last place
+ * aside from a user that could have kicked off I/O. So finally wait for
+ * any worker threads.
+ */
+ if (ccid->ccid_taskq != NULL) {
+ ddi_taskq_wait(ccid->ccid_taskq);
+ mutex_enter(&ccid->ccid_mutex);
+ VERIFY0(ccid->ccid_flags & CCID_F_WORKER_MASK);
+ mutex_exit(&ccid->ccid_mutex);
+ }
+
+ if ((ccid->ccid_attach & CCID_ATTACH_HOTPLUG_CB) != 0) {
+ usb_unregister_event_cbs(dip, &ccid_usb_events);
+ ccid->ccid_attach &= ~CCID_ATTACH_HOTPLUG_CB;
+ }
+
+ if ((ccid->ccid_attach & CCID_ATTACH_SLOTS) != 0) {
+ ccid_slots_fini(ccid);
+ ccid->ccid_attach &= ~CCID_ATTACH_SLOTS;
+ }
+
+ if ((ccid->ccid_attach & CCID_ATTACH_SEQ_IDS) != 0) {
+ id_space_destroy(ccid->ccid_seqs);
+ ccid->ccid_seqs = NULL;
+ ccid->ccid_attach &= ~CCID_ATTACH_SEQ_IDS;
+ }
+
+ if ((ccid->ccid_attach & CCID_ATTACH_OPEN_PIPES) != 0) {
+ usb_pipe_close(dip, ccid->ccid_bulkin_pipe, USB_FLAGS_SLEEP,
+ NULL, NULL);
+ ccid->ccid_bulkin_pipe = NULL;
+ usb_pipe_close(dip, ccid->ccid_bulkout_pipe, USB_FLAGS_SLEEP,
+ NULL, NULL);
+ ccid->ccid_bulkout_pipe = NULL;
+ if ((ccid->ccid_flags & CCID_F_HAS_INTR) != 0) {
+ usb_pipe_close(dip, ccid->ccid_intrin_pipe,
+ USB_FLAGS_SLEEP, NULL, NULL);
+ ccid->ccid_intrin_pipe = NULL;
+ } else {
+ VERIFY3P(ccid->ccid_intrin_pipe, ==, NULL);
+ }
+ ccid->ccid_control_pipe = NULL;
+ ccid->ccid_attach &= ~CCID_ATTACH_OPEN_PIPES;
+ }
+
+ /*
+ * Now that all of the pipes are closed. If we happened to have any
+ * cached bulk requests, we should free them.
+ */
+ ccid_cleanup_bulkin(ccid);
+
+ if (ccid->ccid_attach & CCID_ATTACH_CMD_LIST) {
+ ccid_command_t *cc;
+
+ while ((cc = list_remove_head(&ccid->ccid_command_queue)) !=
+ NULL) {
+ ccid_command_free(cc);
+ }
+ list_destroy(&ccid->ccid_command_queue);
+
+ while ((cc = list_remove_head(&ccid->ccid_complete_queue)) !=
+ NULL) {
+ ccid_command_free(cc);
+ }
+ list_destroy(&ccid->ccid_complete_queue);
+ }
+
+ if ((ccid->ccid_attach & CCID_ATTACH_TASKQ) != 0) {
+ ddi_taskq_destroy(ccid->ccid_taskq);
+ ccid->ccid_taskq = NULL;
+ ccid->ccid_attach &= ~CCID_ATTACH_TASKQ;
+ }
+
+ if ((ccid->ccid_attach & CCID_ATTACH_MUTEX_INIT) != 0) {
+ mutex_destroy(&ccid->ccid_mutex);
+ ccid->ccid_attach &= ~CCID_ATTACH_MUTEX_INIT;
+ }
+
+ if ((ccid->ccid_attach & CCID_ATTACH_USB_CLIENT) != 0) {
+ usb_client_detach(dip, ccid->ccid_dev_data);
+ ccid->ccid_dev_data = NULL;
+ ccid->ccid_attach &= ~CCID_ATTACH_USB_CLIENT;
+ }
+
+ ASSERT0(ccid->ccid_attach);
+ ddi_soft_state_free(ccid_softstate, inst);
+}
+
+static int
+ccid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ ccid_t *ccid;
+ int inst, ret;
+ char buf[64];
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ inst = ddi_get_instance(dip);
+ if (ddi_soft_state_zalloc(ccid_softstate, inst) != DDI_SUCCESS) {
+ ccid_error(NULL, "!failed to allocate soft state for ccid "
+ "instance %d", inst);
+ return (DDI_FAILURE);
+ }
+
+ ccid = ddi_get_soft_state(ccid_softstate, inst);
+ ccid->ccid_dip = dip;
+
+ if ((ret = usb_client_attach(dip, USBDRV_VERSION, 0)) != USB_SUCCESS) {
+ ccid_error(ccid, "!failed to attach to usb client: %d", ret);
+ goto cleanup;
+ }
+ ccid->ccid_attach |= CCID_ATTACH_USB_CLIENT;
+
+ if ((ret = usb_get_dev_data(dip, &ccid->ccid_dev_data, USB_PARSE_LVL_IF,
+ 0)) != USB_SUCCESS) {
+ ccid_error(ccid, "!failed to get usb device data: %d", ret);
+ goto cleanup;
+ }
+
+ mutex_init(&ccid->ccid_mutex, NULL, MUTEX_DRIVER,
+ ccid->ccid_dev_data->dev_iblock_cookie);
+ ccid->ccid_attach |= CCID_ATTACH_MUTEX_INIT;
+
+ (void) snprintf(buf, sizeof (buf), "ccid%d_taskq", inst);
+ ccid->ccid_taskq = ddi_taskq_create(dip, buf, 1, TASKQ_DEFAULTPRI, 0);
+ if (ccid->ccid_taskq == NULL) {
+ ccid_error(ccid, "!failed to create CCID taskq");
+ goto cleanup;
+ }
+ ccid->ccid_attach |= CCID_ATTACH_TASKQ;
+
+ list_create(&ccid->ccid_command_queue, sizeof (ccid_command_t),
+ offsetof(ccid_command_t, cc_list_node));
+ list_create(&ccid->ccid_complete_queue, sizeof (ccid_command_t),
+ offsetof(ccid_command_t, cc_list_node));
+
+ if (!ccid_parse_class_desc(ccid)) {
+ ccid_error(ccid, "!failed to parse CCID class descriptor");
+ goto cleanup;
+ }
+
+ if (!ccid_supported(ccid)) {
+ ccid_error(ccid,
+ "!CCID reader is not supported, not attaching");
+ goto cleanup;
+ }
+
+ if (!ccid_open_pipes(ccid)) {
+ ccid_error(ccid, "!failed to open CCID pipes, not attaching");
+ goto cleanup;
+ }
+ ccid->ccid_attach |= CCID_ATTACH_OPEN_PIPES;
+
+ (void) snprintf(buf, sizeof (buf), "ccid%d_seqs", inst);
+ if ((ccid->ccid_seqs = id_space_create(buf, CCID_SEQ_MIN,
+ CCID_SEQ_MAX + 1)) == NULL) {
+ ccid_error(ccid, "!failed to create CCID sequence id space");
+ goto cleanup;
+ }
+ ccid->ccid_attach |= CCID_ATTACH_SEQ_IDS;
+
+ if (!ccid_slots_init(ccid)) {
+ ccid_error(ccid, "!failed to initialize CCID slot structures");
+ goto cleanup;
+ }
+ ccid->ccid_attach |= CCID_ATTACH_SLOTS;
+
+ if (usb_register_event_cbs(dip, &ccid_usb_events, 0) != USB_SUCCESS) {
+ ccid_error(ccid, "!failed to register USB hotplug callbacks");
+ goto cleanup;
+ }
+ ccid->ccid_attach |= CCID_ATTACH_HOTPLUG_CB;
+
+ /*
+ * Before we enable the interrupt pipe, take a shot at priming our
+ * bulkin_cache.
+ */
+ mutex_enter(&ccid->ccid_mutex);
+ ccid_bulkin_cache_refresh(ccid);
+ mutex_exit(&ccid->ccid_mutex);
+
+ if (ccid->ccid_flags & CCID_F_HAS_INTR) {
+ ccid_intr_poll_init(ccid);
+ }
+ ccid->ccid_attach |= CCID_ATTACH_INTR_ACTIVE;
+
+ /*
+ * Create minor nodes for each slot.
+ */
+ if (!ccid_minors_init(ccid)) {
+ ccid_error(ccid, "!failed to create minor nodes");
+ goto cleanup;
+ }
+ ccid->ccid_attach |= CCID_ATTACH_MINORS;
+
+ mutex_enter(&ccid->ccid_mutex);
+ ccid_worker_request(ccid);
+ mutex_exit(&ccid->ccid_mutex);
+
+ return (DDI_SUCCESS);
+
+cleanup:
+ ccid_cleanup(dip);
+ return (DDI_FAILURE);
+}
+
+static int
+ccid_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **outp)
+{
+ return (DDI_FAILURE);
+}
+
+static int
+ccid_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int inst;
+ ccid_t *ccid;
+
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ inst = ddi_get_instance(dip);
+ ccid = ddi_get_soft_state(ccid_softstate, inst);
+ VERIFY3P(ccid, !=, NULL);
+ VERIFY3P(dip, ==, ccid->ccid_dip);
+
+ mutex_enter(&ccid->ccid_mutex);
+
+ /*
+ * If the device hasn't been disconnected from a USB sense, refuse to
+ * detach. Otherwise, there's no way to guarantee that the ccid
+ * driver will be attached when a user hotplugs an ICC.
+ */
+ if ((ccid->ccid_flags & CCID_F_DISCONNECTED) == 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (DDI_FAILURE);
+ }
+
+ if (!list_is_empty(&ccid->ccid_command_queue) ||
+ !list_is_empty(&ccid->ccid_complete_queue)) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (DDI_FAILURE);
+ }
+ mutex_exit(&ccid->ccid_mutex);
+
+ ccid_cleanup(dip);
+ return (DDI_SUCCESS);
+}
+
+static void
+ccid_minor_free(ccid_minor_t *cmp)
+{
+ VERIFY3U(cmp->cm_idx.cmi_minor, ==, CCID_MINOR_INVALID);
+ crfree(cmp->cm_opener);
+ cv_destroy(&cmp->cm_iowait_cv);
+ cv_destroy(&cmp->cm_read_cv);
+ cv_destroy(&cmp->cm_excl_cv);
+ kmem_free(cmp, sizeof (ccid_minor_t));
+
+}
+
+static int
+ccid_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+ ccid_minor_idx_t *idx;
+ ccid_minor_t *cmp;
+ ccid_slot_t *slot;
+
+ /*
+ * Always check the zone first, to make sure we lie about it existing.
+ */
+ if (crgetzoneid(credp) != GLOBAL_ZONEID)
+ return (ENOENT);
+
+ if ((otyp & (FNDELAY | FEXCL)) != 0)
+ return (EINVAL);
+
+ if (drv_priv(credp) != 0)
+ return (EPERM);
+
+ if (otyp != OTYP_CHR)
+ return (ENOTSUP);
+
+ if ((flag & FREAD) != FREAD)
+ return (EINVAL);
+
+ idx = ccid_minor_find(getminor(*devp));
+ if (idx == NULL) {
+ return (ENOENT);
+ }
+
+ /*
+ * We don't expect anyone to be able to get a non-slot related minor. If
+ * that somehow happens, guard against it and error out.
+ */
+ if (!idx->cmi_isslot) {
+ return (ENOENT);
+ }
+
+ slot = idx->cmi_data.cmi_slot;
+
+ mutex_enter(&slot->cs_ccid->ccid_mutex);
+ if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (ENODEV);
+ }
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+
+ cmp = kmem_zalloc(sizeof (ccid_minor_t), KM_SLEEP);
+
+ cmp->cm_idx.cmi_minor = CCID_MINOR_INVALID;
+ cmp->cm_idx.cmi_isslot = B_FALSE;
+ cmp->cm_idx.cmi_data.cmi_user = cmp;
+ if (!ccid_minor_idx_alloc(&cmp->cm_idx, B_FALSE)) {
+ kmem_free(cmp, sizeof (ccid_minor_t));
+ return (ENOSPC);
+ }
+ cv_init(&cmp->cm_excl_cv, NULL, CV_DRIVER, NULL);
+ cv_init(&cmp->cm_read_cv, NULL, CV_DRIVER, NULL);
+ cv_init(&cmp->cm_iowait_cv, NULL, CV_DRIVER, NULL);
+ cmp->cm_opener = crdup(credp);
+ cmp->cm_slot = slot;
+ *devp = makedevice(getmajor(*devp), cmp->cm_idx.cmi_minor);
+
+ if ((flag & FWRITE) == FWRITE) {
+ cmp->cm_flags |= CCID_MINOR_F_WRITABLE;
+ }
+
+ mutex_enter(&slot->cs_ccid->ccid_mutex);
+ list_insert_tail(&slot->cs_minors, cmp);
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+
+ return (0);
+}
+
+/*
+ * Copy a command which may have a message block chain out to the user.
+ */
+static int
+ccid_read_copyout(struct uio *uiop, const mblk_t *mp)
+{
+ offset_t off;
+
+ off = uiop->uio_loffset;
+ VERIFY3P(mp->b_next, ==, NULL);
+
+ for (; mp != NULL; mp = mp->b_cont) {
+ int ret;
+
+ if (MBLKL(mp) == 0)
+ continue;
+
+ ret = uiomove(mp->b_rptr, MBLKL(mp), UIO_READ, uiop);
+ if (ret != 0) {
+ return (EFAULT);
+ }
+ }
+
+ uiop->uio_loffset = off;
+ return (0);
+}
+
+/*
+ * Called to indicate that we are ready for a user to consume the I/O.
+ */
+static void
+ccid_user_io_done(ccid_t *ccid, ccid_slot_t *slot)
+{
+ ccid_minor_t *cmp;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ slot->cs_io.ci_flags &= ~CCID_IO_F_IN_PROGRESS;
+ slot->cs_io.ci_flags |= CCID_IO_F_DONE;
+ cmp = slot->cs_excl_minor;
+ if (cmp != NULL) {
+ ccid_slot_pollin_signal(slot);
+ cv_signal(&cmp->cm_read_cv);
+ }
+}
+
+/*
+ * This is called in a few different sitautions. It's called when an exclusive
+ * hold is being released by a user on the slot. It's also called when the ICC
+ * is removed, the reader has been unplugged, or the ICC is being reset. In all
+ * these cases we need to make sure that I/O is taken care of and we won't be
+ * leaving behind vestigial garbage.
+ */
+static void
+ccid_teardown_apdu(ccid_t *ccid, ccid_slot_t *slot, int error)
+{
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ /*
+ * If no I/O is in progress, then there's nothing to do at our end.
+ */
+ if ((slot->cs_io.ci_flags & CCID_IO_F_IN_PROGRESS) == 0) {
+ return;
+ }
+
+ slot->cs_io.ci_errno = error;
+ ccid_user_io_done(ccid, slot);
+
+ /*
+ * There is still I/O going on. We need to mark this on the slot such
+ * that no one can gain ownership of it or issue commands. This will
+ * block hand off of a slot.
+ */
+ slot->cs_flags |= CCID_SLOT_F_NEED_IO_TEARDOWN;
+}
+
+/*
+ * This function is called in response to a CCID command completing.
+ */
+static void
+ccid_complete_apdu(ccid_t *ccid, ccid_slot_t *slot, ccid_command_t *cc)
+{
+ ccid_reply_command_status_t crs;
+ ccid_reply_icc_status_t cis;
+ ccid_command_err_t cce;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+ VERIFY3P(slot->cs_io.ci_command, ==, cc);
+
+ /*
+ * This completion could be called due to the fact that a user is no
+ * longer present, but we still have outstanding work to do in the
+ * stack. As such, we need to go through and check if the flag was set
+ * on the slot during teardown and if so, clean it up now.
+ */
+ if ((slot->cs_flags & CCID_SLOT_F_NEED_IO_TEARDOWN) != 0) {
+ ccid_command_free(cc);
+ slot->cs_io.ci_command = NULL;
+ ccid_slot_io_teardown_done(slot);
+ return;
+ }
+
+ /*
+ * Process this command and figure out what we should logically be
+ * returning to the user.
+ */
+ if (cc->cc_state != CCID_COMMAND_COMPLETE) {
+ slot->cs_io.ci_errno = EIO;
+ slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
+ ccid_worker_request(ccid);
+ goto consume;
+ }
+
+ ccid_command_status_decode(cc, &crs, &cis, &cce);
+ if (crs == CCID_REPLY_STATUS_COMPLETE) {
+ mblk_t *mp;
+
+ mp = cc->cc_response;
+ cc->cc_response = NULL;
+ mp->b_rptr += sizeof (ccid_header_t);
+ slot->cs_io.ci_errno = 0;
+ slot->cs_io.ci_data = mp;
+ } else if (cis == CCID_REPLY_ICC_MISSING) {
+ slot->cs_io.ci_errno = ENXIO;
+ } else {
+ /*
+ * There are a few more semantic things we can do
+ * with the errors here that we're throwing out and
+ * lumping as EIO. Oh well.
+ */
+ slot->cs_io.ci_errno = EIO;
+ }
+
+ /*
+ * Now, we can go ahead and wake up a reader to process this command.
+ */
+consume:
+ slot->cs_io.ci_command = NULL;
+ ccid_command_free(cc);
+ ccid_user_io_done(ccid, slot);
+}
+
+/*
+ * We have the user buffer in the CCID slot. Given that, transform it into
+ * something that we can send to the device. For APDU's this is simply creating
+ * a transfer command and copying it into that buffer.
+ */
+static int
+ccid_write_apdu(ccid_t *ccid, ccid_slot_t *slot)
+{
+ int ret;
+ ccid_command_t *cc;
+
+ VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
+
+ if ((ret = ccid_command_alloc(ccid, slot, B_FALSE, NULL,
+ slot->cs_io.ci_ilen, CCID_REQUEST_TRANSFER_BLOCK, 0, 0, 0,
+ &cc)) != 0) {
+ return (ret);
+ }
+
+ cc->cc_flags |= CCID_COMMAND_F_USER;
+ ccid_command_bcopy(cc, slot->cs_io.ci_ibuf, slot->cs_io.ci_ilen);
+
+ slot->cs_io.ci_command = cc;
+ mutex_exit(&ccid->ccid_mutex);
+
+ if ((ret = ccid_command_queue(ccid, cc)) != 0) {
+ mutex_enter(&ccid->ccid_mutex);
+ slot->cs_io.ci_command = NULL;
+ ccid_command_free(cc);
+ return (ret);
+ }
+
+ mutex_enter(&ccid->ccid_mutex);
+
+ return (0);
+}
+
+static int
+ccid_read(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ int ret;
+ ccid_minor_idx_t *idx;
+ ccid_minor_t *cmp;
+ ccid_slot_t *slot;
+ ccid_t *ccid;
+ boolean_t done;
+
+ if (uiop->uio_resid <= 0) {
+ return (EINVAL);
+ }
+
+ if ((idx = ccid_minor_find_user(getminor(dev))) == NULL) {
+ return (ENOENT);
+ }
+
+ cmp = idx->cmi_data.cmi_user;
+ slot = cmp->cm_slot;
+ ccid = slot->cs_ccid;
+
+ mutex_enter(&ccid->ccid_mutex);
+ if ((ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (ENODEV);
+ }
+
+ /*
+ * First, check if we have exclusive access. If not, we're done.
+ */
+ if ((cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) == 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (EACCES);
+ }
+
+ /*
+ * While it's tempting to mirror ccid_write() here and check if we have
+ * a tx or rx function, that actually has no relevance on read. The only
+ * thing that matters is whether or not we actually have an I/O.
+ */
+
+ /*
+ * If there's been no write I/O issued, then this read is not allowed.
+ * While this may seem like a silly constraint, it certainly simplifies
+ * a lot of the surrounding logic and fits with the current consumer
+ * model.
+ */
+ if ((slot->cs_io.ci_flags & (CCID_IO_F_IN_PROGRESS | CCID_IO_F_DONE))
+ == 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (ENODATA);
+ }
+
+ /*
+ * If another thread is already blocked in read, then don't allow us
+ * in. We only want to allow one thread to attempt to consume a read,
+ * just as we only allow one thread to initiate a write.
+ */
+ if ((cmp->cm_flags & CCID_MINOR_F_READ_WAITING) != 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (EBUSY);
+ }
+
+ /*
+ * Check if an I/O has completed. Once it has, call the protocol
+ * specific code. Note that the lock may be dropped after polling. In
+ * such a case we will have to logically recheck several conditions.
+ *
+ * Note, we don't really care if the slot is active or not as I/O could
+ * have been in flight while the slot was inactive.
+ */
+ while ((slot->cs_io.ci_flags & CCID_IO_F_DONE) == 0) {
+ if (uiop->uio_fmode & FNONBLOCK) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (EWOULDBLOCK);
+ }
+
+ /*
+ * While we perform a cv_wait_sig() we'll end up dropping the
+ * CCID mutex. This means that we need to notify the rest of the
+ * driver that a thread is blocked in read. This is used not
+ * only for excluding multiple threads trying to read from the
+ * device, but more importantly so that we know that if the ICC
+ * or reader are removed, that we need to wake up this thread.
+ */
+ cmp->cm_flags |= CCID_MINOR_F_READ_WAITING;
+ ret = cv_wait_sig(&cmp->cm_read_cv, &ccid->ccid_mutex);
+ cmp->cm_flags &= ~CCID_MINOR_F_READ_WAITING;
+ cv_signal(&cmp->cm_iowait_cv);
+
+ if (ret == 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (EINTR);
+ }
+
+ /*
+ * Check if the reader has been removed. We do not need to check
+ * for other conditions, as we'll end up being told that the I/O
+ * is done and that the error has been set.
+ */
+ if ((ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (ENODEV);
+ }
+ }
+
+ /*
+ * We'll either have an error or data available for the user at this
+ * point that we can copy out. We need to make sure that it's not too
+ * large. The data should have already been adjusted such that we only
+ * have data payloads.
+ */
+ done = B_FALSE;
+ if (slot->cs_io.ci_errno == 0) {
+ size_t mlen;
+
+ mlen = msgsize(slot->cs_io.ci_data);
+ if (mlen > uiop->uio_resid) {
+ ret = EOVERFLOW;
+ } else {
+ if ((ret = ccid_read_copyout(uiop, slot->cs_io.ci_data))
+ == 0) {
+ done = B_TRUE;
+ }
+ }
+ } else {
+ ret = slot->cs_io.ci_errno;
+ done = B_TRUE;
+ }
+
+ if (done) {
+ ccid_clear_io(&slot->cs_io);
+ ccid_slot_pollout_signal(slot);
+ }
+
+ mutex_exit(&ccid->ccid_mutex);
+
+ return (ret);
+}
+
+static int
+ccid_write(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ int ret;
+ ccid_minor_idx_t *idx;
+ ccid_minor_t *cmp;
+ ccid_slot_t *slot;
+ ccid_t *ccid;
+ size_t len, cbytes;
+
+ if (uiop->uio_resid > CCID_APDU_LEN_MAX) {
+ return (E2BIG);
+ }
+
+ if (uiop->uio_resid <= 0) {
+ return (EINVAL);
+ }
+
+ len = uiop->uio_resid;
+ idx = ccid_minor_find_user(getminor(dev));
+ if (idx == NULL) {
+ return (ENOENT);
+ }
+
+ cmp = idx->cmi_data.cmi_user;
+ slot = cmp->cm_slot;
+ ccid = slot->cs_ccid;
+
+ /*
+ * Now that we have the slot, verify whether or not we can perform this
+ * I/O.
+ */
+ mutex_enter(&ccid->ccid_mutex);
+ if ((ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (ENODEV);
+ }
+
+ /*
+ * Check that we are open for writing, have exclusive access, and
+ * there's a card present. If not, error out.
+ */
+ if ((cmp->cm_flags & (CCID_MINOR_F_WRITABLE | CCID_MINOR_F_HAS_EXCL)) !=
+ (CCID_MINOR_F_WRITABLE | CCID_MINOR_F_HAS_EXCL)) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (EACCES);
+ }
+
+ if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) == 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (ENXIO);
+ }
+
+ /*
+ * Make sure that we have a supported transmit function.
+ */
+ if (slot->cs_icc.icc_tx == NULL) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (ENOTSUP);
+ }
+
+ /*
+ * See if another command is in progress. If so, try to claim it.
+ * Otherwise, fail with EBUSY. Note, we only fail for commands that are
+ * user initiated. There may be other commands that are ongoing in the
+ * system.
+ */
+ if ((slot->cs_io.ci_flags & CCID_IO_F_POLLOUT_FLAGS) != 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (EBUSY);
+ }
+
+ /*
+ * Use uiocopy and not uiomove. This way if we fail for whatever reason,
+ * we don't have to worry about restoring the original buffer.
+ */
+ if (uiocopy(slot->cs_io.ci_ibuf, len, UIO_WRITE, uiop, &cbytes) != 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (EFAULT);
+ }
+
+ slot->cs_io.ci_ilen = len;
+ slot->cs_io.ci_flags |= CCID_IO_F_PREPARING;
+ slot->cs_io.ci_omp = NULL;
+
+ /*
+ * Now that we're here, go ahead and call the actual tx function.
+ */
+ if ((ret = slot->cs_icc.icc_tx(ccid, slot)) != 0) {
+ /*
+ * The command wasn't actually transmitted. In this case we need
+ * to reset the copied in data and signal anyone who is polling
+ * that this is writeable again. We don't have to worry about
+ * readers at this point, as they won't get in unless
+ * CCID_IO_F_IN_PROGRESS has been set.
+ */
+ slot->cs_io.ci_ilen = 0;
+ bzero(slot->cs_io.ci_ibuf, sizeof (slot->cs_io.ci_ibuf));
+ slot->cs_io.ci_flags &= ~CCID_IO_F_PREPARING;
+
+ ccid_slot_pollout_signal(slot);
+ } else {
+ slot->cs_io.ci_flags &= ~CCID_IO_F_PREPARING;
+ slot->cs_io.ci_flags |= CCID_IO_F_IN_PROGRESS;
+ uiop->uio_resid -= cbytes;
+ }
+ /*
+ * Notify a waiter that we've moved on.
+ */
+ cv_signal(&slot->cs_excl_minor->cm_iowait_cv);
+ mutex_exit(&ccid->ccid_mutex);
+
+ return (ret);
+}
+
+static int
+ccid_ioctl_status(ccid_slot_t *slot, intptr_t arg, int mode)
+{
+ uccid_cmd_status_t ucs;
+ ccid_t *ccid = slot->cs_ccid;
+
+ if (ddi_copyin((void *)arg, &ucs, sizeof (ucs), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (ucs.ucs_version != UCCID_CURRENT_VERSION)
+ return (EINVAL);
+
+ ucs.ucs_status = 0;
+ ucs.ucs_instance = ddi_get_instance(slot->cs_ccid->ccid_dip);
+ ucs.ucs_slot = slot->cs_slotno;
+
+ mutex_enter(&slot->cs_ccid->ccid_mutex);
+ if ((slot->cs_flags & CCID_SLOT_F_PRESENT) != 0)
+ ucs.ucs_status |= UCCID_STATUS_F_CARD_PRESENT;
+ if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) != 0)
+ ucs.ucs_status |= UCCID_STATUS_F_CARD_ACTIVE;
+
+ if (slot->cs_atr != NULL) {
+ ucs.ucs_atrlen = MIN(UCCID_ATR_MAX, MBLKL(slot->cs_atr));
+ bcopy(slot->cs_atr->b_rptr, ucs.ucs_atr, ucs.ucs_atrlen);
+ } else {
+ bzero(ucs.ucs_atr, sizeof (ucs.ucs_atr));
+ ucs.ucs_atrlen = 0;
+ }
+
+ bcopy(&ccid->ccid_class, &ucs.ucs_class, sizeof (ucs.ucs_class));
+
+ if (ccid->ccid_dev_data->dev_product != NULL) {
+ (void) strlcpy(ucs.ucs_product,
+ ccid->ccid_dev_data->dev_product, sizeof (ucs.ucs_product));
+ ucs.ucs_status |= UCCID_STATUS_F_PRODUCT_VALID;
+ } else {
+ ucs.ucs_product[0] = '\0';
+ }
+
+ if (ccid->ccid_dev_data->dev_serial != NULL) {
+ (void) strlcpy(ucs.ucs_serial, ccid->ccid_dev_data->dev_serial,
+ sizeof (ucs.ucs_serial));
+ ucs.ucs_status |= UCCID_STATUS_F_SERIAL_VALID;
+ } else {
+ ucs.ucs_serial[0] = '\0';
+ }
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+
+ if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) != 0) {
+ ucs.ucs_status |= UCCID_STATUS_F_PARAMS_VALID;
+ ucs.ucs_prot = slot->cs_icc.icc_cur_protocol;
+ ucs.ucs_params = slot->cs_icc.icc_params;
+ }
+
+ if (ddi_copyout(&ucs, (void *)arg, sizeof (ucs), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+ccid_ioctl_txn_begin(ccid_slot_t *slot, ccid_minor_t *cmp, intptr_t arg,
+ int mode)
+{
+ int ret;
+ uccid_cmd_txn_begin_t uct;
+ boolean_t nowait;
+
+ if (ddi_copyin((void *)arg, &uct, sizeof (uct), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (uct.uct_version != UCCID_CURRENT_VERSION)
+ return (EINVAL);
+
+ if ((uct.uct_flags & ~UCCID_TXN_DONT_BLOCK) != 0)
+ return (EINVAL);
+ nowait = (uct.uct_flags & UCCID_TXN_DONT_BLOCK) != 0;
+
+ mutex_enter(&slot->cs_ccid->ccid_mutex);
+ if ((cmp->cm_flags & CCID_MINOR_F_WRITABLE) == 0) {
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (EACCES);
+ }
+
+ ret = ccid_slot_excl_req(slot, cmp, nowait);
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+
+ return (ret);
+}
+
+static int
+ccid_ioctl_txn_end(ccid_slot_t *slot, ccid_minor_t *cmp, intptr_t arg, int mode)
+{
+ uccid_cmd_txn_end_t uct;
+
+ if (ddi_copyin((void *)arg, &uct, sizeof (uct), mode & FKIOCTL) != 0) {
+ return (EFAULT);
+ }
+
+ if (uct.uct_version != UCCID_CURRENT_VERSION) {
+ return (EINVAL);
+ }
+
+ mutex_enter(&slot->cs_ccid->ccid_mutex);
+ if (slot->cs_excl_minor != cmp) {
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (EINVAL);
+ }
+ VERIFY3S(cmp->cm_flags & CCID_MINOR_F_HAS_EXCL, !=, 0);
+
+ /*
+ * Require exactly one of the flags to be set.
+ */
+ switch (uct.uct_flags) {
+ case UCCID_TXN_END_RESET:
+ cmp->cm_flags |= CCID_MINOR_F_TXN_RESET;
+
+ case UCCID_TXN_END_RELEASE:
+ break;
+
+ default:
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (EINVAL);
+ }
+
+ ccid_slot_excl_rele(slot);
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+
+ return (0);
+}
+
+static int
+ccid_ioctl_fionread(ccid_slot_t *slot, ccid_minor_t *cmp, intptr_t arg,
+ int mode)
+{
+ int data;
+
+ mutex_enter(&slot->cs_ccid->ccid_mutex);
+ if ((cmp->cm_flags & (CCID_MINOR_F_WRITABLE | CCID_MINOR_F_HAS_EXCL)) ==
+ (CCID_MINOR_F_WRITABLE | CCID_MINOR_F_HAS_EXCL)) {
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (EACCES);
+ }
+
+ if ((slot->cs_io.ci_flags & CCID_IO_F_DONE) != 0) {
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (ENODATA);
+ }
+
+ /*
+ * If there's an error, claim that there's at least one byte to read
+ * even if it means we'll get the error and consume it. FIONREAD only
+ * allows up to an int of data. Realistically because we don't allow
+ * extended APDUs, the amount of data here should be always less than
+ * INT_MAX.
+ */
+ if (slot->cs_io.ci_errno != 0) {
+ data = 1;
+ } else {
+ size_t s = msgsize(slot->cs_io.ci_data);
+ data = MIN(s, INT_MAX);
+ }
+
+ if (ddi_copyout(&data, (void *)arg, sizeof (data), mode & FKIOCTL)
+ != 0) {
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (EFAULT);
+ }
+
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (0);
+}
+
+static int
+ccid_ioctl_icc_modify(ccid_slot_t *slot, ccid_minor_t *cmp, intptr_t arg,
+ int mode)
+{
+ int ret = 0;
+ uccid_cmd_icc_modify_t uci;
+ ccid_t *ccid;
+
+ if (ddi_copyin((void *)arg, &uci, sizeof (uci), mode & FKIOCTL) != 0) {
+ return (EFAULT);
+ }
+
+ if (uci.uci_version != UCCID_CURRENT_VERSION) {
+ return (EINVAL);
+ }
+
+ switch (uci.uci_action) {
+ case UCCID_ICC_POWER_ON:
+ case UCCID_ICC_POWER_OFF:
+ case UCCID_ICC_WARM_RESET:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ ccid = slot->cs_ccid;
+ mutex_enter(&ccid->ccid_mutex);
+ if ((cmp->cm_flags & (CCID_MINOR_F_WRITABLE | CCID_MINOR_F_HAS_EXCL)) !=
+ (CCID_MINOR_F_WRITABLE | CCID_MINOR_F_HAS_EXCL)) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (EACCES);
+ }
+
+ switch (uci.uci_action) {
+ case UCCID_ICC_WARM_RESET:
+ ret = ccid_slot_warm_reset(ccid, slot);
+ break;
+
+ case UCCID_ICC_POWER_OFF:
+ ret = ccid_slot_power_off(ccid, slot);
+ break;
+
+ case UCCID_ICC_POWER_ON:
+ ret = ccid_slot_inserted(ccid, slot);
+ break;
+ }
+
+ mutex_exit(&ccid->ccid_mutex);
+
+ return (ret);
+}
+
+static int
+ccid_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ ccid_minor_idx_t *idx;
+ ccid_slot_t *slot;
+ ccid_minor_t *cmp;
+
+ idx = ccid_minor_find_user(getminor(dev));
+ if (idx == NULL) {
+ return (ENOENT);
+ }
+
+ cmp = idx->cmi_data.cmi_user;
+ slot = cmp->cm_slot;
+
+ mutex_enter(&slot->cs_ccid->ccid_mutex);
+ if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+ return (ENODEV);
+ }
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+
+ switch (cmd) {
+ case UCCID_CMD_TXN_BEGIN:
+ return (ccid_ioctl_txn_begin(slot, cmp, arg, mode));
+ case UCCID_CMD_TXN_END:
+ return (ccid_ioctl_txn_end(slot, cmp, arg, mode));
+ case UCCID_CMD_STATUS:
+ return (ccid_ioctl_status(slot, arg, mode));
+ case FIONREAD:
+ return (ccid_ioctl_fionread(slot, cmp, arg, mode));
+ case UCCID_CMD_ICC_MODIFY:
+ return (ccid_ioctl_icc_modify(slot, cmp, arg, mode));
+ default:
+ break;
+ }
+
+ return (ENOTTY);
+}
+
+static int
+ccid_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ short ready = 0;
+ ccid_minor_idx_t *idx;
+ ccid_minor_t *cmp;
+ ccid_slot_t *slot;
+ ccid_t *ccid;
+
+ idx = ccid_minor_find_user(getminor(dev));
+ if (idx == NULL) {
+ return (ENOENT);
+ }
+
+ cmp = idx->cmi_data.cmi_user;
+ slot = cmp->cm_slot;
+ ccid = slot->cs_ccid;
+
+ mutex_enter(&ccid->ccid_mutex);
+ if ((ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
+ mutex_exit(&ccid->ccid_mutex);
+ return (ENODEV);
+ }
+
+ if ((cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) != 0) {
+ /*
+ * If the CCID_IO_F_DONE flag is set, then we're always
+ * readable. However, flags are insufficient to be writeable.
+ */
+ if ((slot->cs_io.ci_flags & CCID_IO_F_DONE) != 0) {
+ ready |= POLLIN | POLLRDNORM;
+ } else if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) != 0 &&
+ (slot->cs_io.ci_flags & CCID_IO_F_POLLOUT_FLAGS) == 0 &&
+ slot->cs_icc.icc_tx != NULL) {
+ ready |= POLLOUT;
+ }
+
+ if ((slot->cs_flags & CCID_SLOT_F_PRESENT) == 0) {
+ ready |= POLLHUP;
+ }
+ }
+
+ *reventsp = ready & events;
+ if ((*reventsp == 0 && !anyyet) || (events & POLLET)) {
+ *phpp = &cmp->cm_pollhead;
+ }
+
+ mutex_exit(&ccid->ccid_mutex);
+
+ return (0);
+}
+
+static int
+ccid_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ ccid_minor_idx_t *idx;
+ ccid_minor_t *cmp;
+ ccid_slot_t *slot;
+
+ idx = ccid_minor_find_user(getminor(dev));
+ if (idx == NULL) {
+ return (ENOENT);
+ }
+
+ /*
+ * First tear down the global index entry.
+ */
+ cmp = idx->cmi_data.cmi_user;
+ slot = cmp->cm_slot;
+ ccid_minor_idx_free(idx);
+
+ /*
+ * If the minor node was closed without an explicit transaction end,
+ * then we need to assume that the reader's ICC is in an arbitrary
+ * state. For example, the ICC could have a specific PIV applet
+ * selected. In such a case, the only safe thing to do is to force a
+ * reset.
+ */
+ mutex_enter(&slot->cs_ccid->ccid_mutex);
+ if ((cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) != 0) {
+ cmp->cm_flags |= CCID_MINOR_F_TXN_RESET;
+ ccid_slot_excl_rele(slot);
+ }
+
+ list_remove(&slot->cs_minors, cmp);
+ mutex_exit(&slot->cs_ccid->ccid_mutex);
+
+ pollhead_clean(&cmp->cm_pollhead);
+ ccid_minor_free(cmp);
+
+ return (0);
+}
+
+static struct cb_ops ccid_cb_ops = {
+ ccid_open, /* cb_open */
+ ccid_close, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ ccid_read, /* cb_read */
+ ccid_write, /* cb_write */
+ ccid_ioctl, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ ccid_chpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ NULL, /* cb_stream */
+ D_MP, /* cb_flag */
+ CB_REV, /* cb_rev */
+ nodev, /* cb_aread */
+ nodev /* cb_awrite */
+};
+
+static struct dev_ops ccid_dev_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ ccid_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ ccid_attach, /* devo_attach */
+ ccid_detach, /* devo_detach */
+ nodev, /* devo_reset */
+ &ccid_cb_ops, /* devo_cb_ops */
+ NULL, /* devo_bus_ops */
+ NULL, /* devo_power */
+ ddi_quiesce_not_supported /* devo_quiesce */
+};
+
+static struct modldrv ccid_modldrv = {
+ &mod_driverops,
+ "USB CCID",
+ &ccid_dev_ops
+};
+
+static struct modlinkage ccid_modlinkage = {
+ MODREV_1,
+ { &ccid_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+ int ret;
+
+ if ((ret = ddi_soft_state_init(&ccid_softstate, sizeof (ccid_t),
+ 0)) != 0) {
+ return (ret);
+ }
+
+ if ((ccid_minors = id_space_create("ccid_minors", CCID_MINOR_MIN,
+ INT_MAX)) == NULL) {
+ ddi_soft_state_fini(&ccid_softstate);
+ return (ret);
+ }
+
+ if ((ret = mod_install(&ccid_modlinkage)) != 0) {
+ id_space_destroy(ccid_minors);
+ ccid_minors = NULL;
+ ddi_soft_state_fini(&ccid_softstate);
+ return (ret);
+ }
+
+ mutex_init(&ccid_idxlock, NULL, MUTEX_DRIVER, NULL);
+ avl_create(&ccid_idx, ccid_idx_comparator, sizeof (ccid_minor_idx_t),
+ offsetof(ccid_minor_idx_t, cmi_avl));
+
+ return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&ccid_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int ret;
+
+ if ((ret = mod_remove(&ccid_modlinkage)) != 0) {
+ return (ret);
+ }
+
+ avl_destroy(&ccid_idx);
+ mutex_destroy(&ccid_idxlock);
+ id_space_destroy(ccid_minors);
+ ccid_minors = NULL;
+ ddi_soft_state_fini(&ccid_softstate);
+
+ return (ret);
+}
diff --git a/usr/src/uts/common/os/logsubr.c b/usr/src/uts/common/os/logsubr.c
index 06c03dd38e..9e58a7bb56 100644
--- a/usr/src/uts/common/os/logsubr.c
+++ b/usr/src/uts/common/os/logsubr.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2013 Gary Mills
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
#include <sys/types.h>
@@ -250,7 +250,7 @@ log_init(void)
*/
printf("\rSunOS Release %s Version %s %u-bit\n",
utsname.release, utsname.version, NBBY * (uint_t)sizeof (void *));
- printf("Copyright (c) 2010-2019, Joyent Inc. All rights reserved.\n");
+ printf("Copyright 2010-2020 Joyent, Inc.\n");
#ifdef DEBUG
printf("DEBUG enabled\n");
#endif
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 02d900901c..c340476574 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -1041,6 +1041,10 @@ USBWCMHDRS= \
UGENHDRS= \
usb_ugen.h
+USBCCIDHDRS = \
+ ccid.h \
+ uccid.h
+
HOTPLUGHDRS= \
hpcsvc.h \
hpctrl.h
@@ -1187,6 +1191,7 @@ CHECKHDRS= \
$(SYSEVENTHDRS:%.h=sysevent/%.check) \
$(CONTRACTHDRS:%.h=contract/%.check) \
$(USBAUDHDRS:%.h=usb/clients/audio/%.check) \
+ $(USBCCIDHDRS:%.h=usb/clients/ccid/%.check) \
$(USBHUBDHDRS:%.h=usb/hubd/%.check) \
$(USBHIDHDRS:%.h=usb/clients/hid/%.check) \
$(USBMSHDRS:%.h=usb/clients/mass_storage/%.check) \
@@ -1261,6 +1266,7 @@ CHECKHDRS= \
$(ROOTUSBCDCHDRS) \
$(ROOTUSBVIDHDRS) \
$(ROOTUSBWCMHDRS) \
+ $(ROOTUSBCCIDHDRS) \
$(ROOTUGENHDRS) \
$(ROOT1394HDRS) \
$(ROOTHOTPLUGHDRS) \
@@ -1319,6 +1325,7 @@ install_h: \
$(ROOTUWBHDRS) \
$(ROOTUWBAHDRS) \
$(ROOTUSBHDRS) \
+ $(ROOTUSBCCIDHDRS) \
$(ROOTUSBAUDHDRS) \
$(ROOTUSBHUBDHDRS) \
$(ROOTUSBHIDHDRS) \
diff --git a/usr/src/uts/common/sys/Makefile.syshdrs b/usr/src/uts/common/sys/Makefile.syshdrs
index dee5eef53a..54a89a5e9a 100644
--- a/usr/src/uts/common/sys/Makefile.syshdrs
+++ b/usr/src/uts/common/sys/Makefile.syshdrs
@@ -23,6 +23,7 @@
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
# Copyright 2016 Nexenta Systems, Inc.
+# Copyright 2019 Joyent, Inc.
#
# Common definitions for open and closed headers.
@@ -149,6 +150,9 @@ usb/clients/mass_storage/%.check: usb/clients/mass_storage/%.h
usb/clients/printer/%.check: usb/clients/printer/%.h
$(DOT_H_CHECK)
+usb/clients/ccid/%.check: usb/clients/ccid/%.h
+ $(DOT_H_CHECK)
+
usb/clients/usbcdc/%.check: usb/clients/usbcdc/%.h
$(DOT_H_CHECK)
@@ -231,6 +235,7 @@ ROOTDIRS= \
$(ROOTDIR)/usb/clients/usbcdc \
$(ROOTDIR)/usb/clients/video/usbvc \
$(ROOTDIR)/usb/clients/usbinput/usbwcm \
+ $(ROOTDIR)/usb/clients/ccid \
$(ROOTDIR)/usb/clients/ugen \
$(ROOTDIR)/1394 \
$(ROOTDIR)/rsm \
@@ -311,6 +316,7 @@ ROOTUSBPRNHDRS= $(USBPRNHDRS:%=$(ROOTDIR)/usb/clients/printer/%)
ROOTUSBCDCHDRS= $(USBCDCHDRS:%=$(ROOTDIR)/usb/clients/usbcdc/%)
ROOTUSBVIDHDRS= $(USBVIDHDRS:%=$(ROOTDIR)/usb/clients/video/usbvc/%)
ROOTUSBWCMHDRS= $(USBWCMHDRS:%=$(ROOTDIR)/usb/clients/usbinput/usbwcm/%)
+ROOTUSBCCIDHDRS= $(USBCCIDHDRS:%=$(ROOTDIR)/usb/clients/ccid/%)
ROOTUGENHDRS= $(UGENHDRS:%=$(ROOTDIR)/usb/clients/ugen/%)
ROOT1394HDRS= $(I1394HDRS:%=$(ROOTDIR)/1394/%)
diff --git a/usr/src/uts/common/sys/smbios.h b/usr/src/uts/common/sys/smbios.h
index 43163a7507..82a81b5a28 100644
--- a/usr/src/uts/common/sys/smbios.h
+++ b/usr/src/uts/common/sys/smbios.h
@@ -156,6 +156,7 @@ typedef union {
#define SMB_TYPE_OBDEVEXT 41 /* on-board device extended info */
#define SMB_TYPE_MCHI 42 /* mgmt controller host interface */
#define SMB_TYPE_TPM 43 /* TPM device */
+#define SMB_TYPE_PROCESSOR_INFO 44 /* Processor Additional Information */
#define SMB_TYPE_INACTIVE 126 /* inactive table entry */
#define SMB_TYPE_EOT 127 /* end of table */
@@ -438,10 +439,6 @@ typedef struct smbios_processor {
/* number of threads per processor socket */
uint16_t smbp_cflags;
/* processor characteristics (SMB_PRC_*) */
- uint16_t smbp_family2; /* processor family 2 */
- uint16_t smbp_corecount2; /* core count 2 */
- uint16_t smbp_coresenabled2; /* cores enabled 2 */
- uint16_t smbp_threadcount2; /* thread count 2 */
} smbios_processor_t;
#define SMB_PRT_OTHER 0x01 /* other */
@@ -539,6 +536,7 @@ typedef struct smbios_processor {
#define SMB_PRC_NX 0x0020 /* execution protection */
#define SMB_PRC_VT 0x0040 /* enhanced virtualization */
#define SMB_PRC_PM 0x0080 /* power/performance control */
+#define SMB_PRC_128BIT 0x0100 /* 128-bit capable */
#define SMB_PRF_OTHER 0x01 /* other */
#define SMB_PRF_UNKNOWN 0x02 /* unknown */
@@ -752,6 +750,9 @@ typedef struct smbios_processor {
#define SMB_PRF_WINCHIP 0x140 /* WinChip */
#define SMB_PRF_DSP 0x15E /* DSP */
#define SMB_PRF_VIDEO 0x1F4 /* Video Processor */
+#define SMB_PRF_RV32 0x200 /* RISC-V RV32 */
+#define SMB_PRF_RV64 0x201 /* RISC-V RV64 */
+#define SMB_PRF_RV128 0x202 /* RISC-V RV128 */
/*
* SMBIOS Cache Information. See DSP0134 Section 7.8 for more information.
@@ -985,6 +986,7 @@ typedef struct smbios_slot {
#define SMB_SLT_PCIE_M52_WOBSKO 0x22 /* PCI Express Mini 52-pin without bottom-side keep-outs */
/* END CSTYLED */
#define SMB_SLT_PCIE_M76 0x23 /* PCI Express Mini 72-pin */
+#define SMB_SLT_CXL1 0x30 /* CXL Flexbus 1.0 */
#define SMB_SLT_PC98_C20 0xA0 /* PC-98/C20 */
#define SMB_SLT_PC98_C24 0xA1 /* PC-98/C24 */
#define SMB_SLT_PC98_E 0xA2 /* PC-98/E */
@@ -1008,6 +1010,12 @@ typedef struct smbios_slot {
#define SMB_SLT_PCIE3G4 0xB4 /* PCI Exp. Gen 3 x4 */
#define SMB_SLT_PCIE3G8 0xB5 /* PCI Exp. Gen 3 x8 */
#define SMB_SLT_PCIE3G16 0xB6 /* PCI Exp. Gen 3 x16 */
+#define SMB_SLT_PCIE4G 0xB8 /* PCI Exp. Gen 4 */
+#define SMB_SLT_PCIE4G1 0xB9 /* PCI Exp. Gen 4 x1 */
+#define SMB_SLT_PCIE4G2 0xBA /* PCI Exp. Gen 4 x2 */
+#define SMB_SLT_PCIE4G4 0xBB /* PCI Exp. Gen 4 x4 */
+#define SMB_SLT_PCIE4G8 0xBC /* PCI Exp. Gen 4 x8 */
+#define SMB_SLT_PCIE4G16 0xBD /* PCI Exp. Gen 4 x16 */
#define SMB_SLW_OTHER 0x01 /* other */
#define SMB_SLW_UNKNOWN 0x02 /* unknown */
@@ -1170,6 +1178,7 @@ typedef struct smbios_memarray {
#define SMB_MAL_PC98C24 0xA1 /* PC-98/C24 add-on card */
#define SMB_MAL_PC98E 0xA2 /* PC-98/E add-on card */
#define SMB_MAL_PC98LB 0xA3 /* PC-98/Local bus add-on card */
+#define SMB_MAL_CXL1 0xA4 /* CXL Flexbus 1.0 add-on card */
#define SMB_MAU_OTHER 0x01 /* other */
#define SMB_MAU_UNKNOWN 0x02 /* unknown */
@@ -1225,6 +1234,8 @@ typedef struct smbios_memdevice {
uint64_t smbmd_volatile_size; /* volatile size in bytes */
uint64_t smbmd_cache_size; /* cache size in bytes */
uint64_t smbmd_logical_size; /* logical size in bytes */
+ uint64_t smbmd_extspeed; /* extended device speed */
+ uint64_t smbmd_extclkspeed; /* extended configured speed */
} smbios_memdevice_t;
#define SMB_MDFF_OTHER 0x01 /* other */
@@ -1242,6 +1253,7 @@ typedef struct smbios_memdevice {
#define SMB_MDFF_SODIMM 0x0D /* SODIMM */
#define SMB_MDFF_SRIMM 0x0E /* SRIMM */
#define SMB_MDFF_FBDIMM 0x0F /* FBDIMM */
+#define SMB_MDFF_DIE 0x10 /* die */
#define SMB_MDT_OTHER 0x01 /* other */
#define SMB_MDT_UNKNOWN 0x02 /* unknown */
@@ -1271,6 +1283,8 @@ typedef struct smbios_memdevice {
#define SMB_MDT_LPDDR3 0x1D /* LPDDR3 */
#define SMB_MDT_LPDDR4 0x1E /* LPDDR4 */
#define SMB_MDT_LOGNV 0x1F /* Logical non-volatile device */
+#define SMB_MDT_HBM 0x20 /* High Bandwidth Memory */
+#define SMB_MDT_HBM2 0x21 /* High Bandwidth Memory 2 */
#define SMB_MDF_OTHER 0x0002 /* other */
#define SMB_MDF_UNKNOWN 0x0004 /* unknown */
@@ -1299,7 +1313,7 @@ typedef struct smbios_memdevice {
#define SMB_MTECH_NVDIMM_N 0x04 /* NVDIMM-N */
#define SMB_MTECH_NVDIMM_F 0x05 /* NVDIMM-F */
#define SMB_MTECH_NVDIMM_P 0x06 /* NVDIMM-P */
-#define SMB_MTECH_INTCPM 0x07 /* Intel persistent memory */
+#define SMB_MTECH_INTCPM 0x07 /* Intel Optane DC Persistent Memory */
#define SMB_MOMC_OTHER 0x01 /* other */
#define SMB_MOMC_UNKNOWN 0x02 /* unknown */
@@ -1333,6 +1347,38 @@ typedef struct smbios_memdevmap {
} smbios_memdevmap_t;
/*
+ * SMBIOS Builtin Pointing Device (SMB_TYPE_POINTDEV). See DSP0134 Sectin 7.22
+ * for more information.
+ */
+typedef struct smbios_pointdev {
+ uint16_t smbpd_type; /* device type */
+ uint16_t smbpd_iface; /* device information */
+ uint8_t smbpd_nbuttons; /* number of buttons */
+} smbios_pointdev_t;
+
+#define SMB_PDT_OTHER 0x01 /* Other */
+#define SMB_PDT_UNKNOWN 0x02 /* Unknown */
+#define SMB_PDT_MOUSE 0x03 /* Mouse */
+#define SMB_PDT_TRBALL 0x04 /* Track Ball */
+#define SMB_PDT_TRPOINT 0x05 /* Track Point */
+#define SMB_PDT_GLPOINT 0x06 /* Glide Point */
+#define SMB_PDT_TOPAD 0x07 /* Touch Pad */
+#define SMB_PDT_TOSCREEN 0x08 /* Touch Screen */
+#define SMB_PDT_OPTSENSE 0x09 /* Optical Sensor */
+
+#define SMB_PDI_OTHER 0x01 /* Other */
+#define SMB_PDI_UNKNOWN 0x02 /* Unknown */
+#define SMB_PDI_SERIAL 0x03 /* Serial */
+#define SMB_PDI_PS2 0x04 /* PS/2 */
+#define SMB_PDI_INFRARED 0x05 /* Infrared */
+#define SMB_PDI_HPHIL 0x06 /* HP-HIL */
+#define SMB_PDI_BUSM 0x07 /* Bus mouse */
+#define SMB_PDI_ADB 0x08 /* ADB (Apple Desktop Bus) */
+#define SMB_PDI_BUSM_DB9 0xA0 /* Bus mouse DB-9 */
+#define SMB_PDI_BUSM_UDIN 0xA1 /* Bus mouse micro-DIN */
+#define SMB_PDI_BUSM_USB 0xA2 /* USB */
+
+/*
* SMBIOS Hardware Security Settings. See DSP0134 Section 7.25 for more
* information. Only one such record will be present in the SMBIOS.
*/
@@ -1609,6 +1655,90 @@ typedef struct smbios_obdev_ext {
uint8_t smboe_df; /* device/function number */
} smbios_obdev_ext_t;
+/*
+ * SMBIOS Processor Additional Information (Type 44). See sectoin 7.45 for more
+ * information.
+ */
+typedef struct smbios_processor_info {
+ id_t smbpi_processor; /* processor handle */
+ uint32_t smbpi_ptype; /* processor type */
+} smbios_processor_info_t;
+
+/* BEGIN CSTYLED */
+#define SMB_PROCINFO_T_RESERVED 0x00 /* reserved */
+#define SMB_PROCINFO_T_IA32 0x01 /* IA32 (x86) */
+#define SMB_PROCINFO_T_AMD64 0x02 /* X64 (x86-64, Intel64, AMD64, EMT64) */
+#define SMB_PROCINFO_T_IA 0x03 /* Intel Itanium architecture */
+#define SMB_PROCINFO_T_AARCH32 0x04 /* 32-bit ARM (aarch32) */
+#define SMB_PROCINFO_T_AARCH64 0x05 /* 64-bit ARM (aarch64) */
+#define SMB_PROCINFO_T_RV32 0x06 /* 32-bit RISC-V (RV32) */
+#define SMB_PROCINFO_T_RV64 0x07 /* 64-bit RISC-V (RV64) */
+#define SMB_PROCINFO_T_RV128 0x08 /* 128-bit RISC-V (RV128) */
+/* END CSTYLED */
+
+typedef struct smbios_processor_info_riscv {
+ uint8_t smbpirv_hartid[16]; /* HART ID */
+ uint8_t smbpirv_vendid[16]; /* Vendor ID */
+ uint8_t smbpirv_archid[16]; /* Architecture ID */
+ uint8_t smbpirv_machid[16]; /* Machine ID */
+ uint8_t smbpirv_metdi[16]; /* Machine exception delegation */
+ uint8_t smbpirv_mitdi[16]; /* Machine interrupt delegation */
+ uint64_t smbpirv_isa; /* Supported ISA */
+ uint32_t smbpirv_privlvl; /* Privilege Level */
+ uint8_t smbpirv_boothart; /* Indicates if boot processor */
+ uint8_t smbpirv_xlen; /* Default register width */
+ uint8_t smbpirv_mxlen; /* Machine register width */
+ uint8_t smbpirv_sxlen; /* Supervisor register width */
+ uint8_t smbpirv_uxlen; /* User register width */
+} smbios_processor_info_riscv_t;
+
+/*
+ * RISC-V Supported Privilege Levels
+ */
+#define SMB_RV_PRIV_M (1 << 0) /* Machine Mode */
+#define SMB_RV_PRIV_S (1 << 2) /* Supervisor Mode */
+#define SMB_RV_PRIV_U (1 << 3) /* User Mode */
+#define SMB_RV_PRIV_DBG (1 << 7) /* Debug Mode */
+
+/*
+ * Values used to define the various XLEN Values.
+ */
+#define SMB_RV_WIDTH_UNSUP 0x00 /* Unsupported */
+#define SMB_RV_WIDTH_32B 0x01 /* 32-bit */
+#define SMB_RV_WIDTH_64B 0x02 /* 64-bit */
+#define SMB_RV_WIDTH_128B 0x03 /* 128-bit */
+
+/*
+ * RISC-V ISA extensions
+ */
+/* BEGIN CSTYLED */
+#define SMB_RV_ISA_A (1 << 0) /* Atomic */
+#define SMB_RV_ISA_B (1 << 1) /* Reserved */
+#define SMB_RV_ISA_C (1 << 2) /* Compressed */
+#define SMB_RV_ISA_D (1 << 3) /* Double-precision floating-point */
+#define SMB_RV_ISA_E (1 << 4) /* RV32E base */
+#define SMB_RV_ISA_F (1 << 5) /* Single-precision floating-point */
+#define SMB_RV_ISA_G (1 << 6) /* Additional standard extensions present */
+#define SMB_RV_ISA_H (1 << 7) /* Hypervisor */
+#define SMB_RV_ISA_I (1 << 8) /* Integer base ISA */
+#define SMB_RV_ISA_J (1 << 9) /* Reserved */
+#define SMB_RV_ISA_K (1 << 10) /* Reserved */
+#define SMB_RV_ISA_L (1 << 11) /* Reserved */
+#define SMB_RV_ISA_M (1 << 12) /* Integer Multiply/Divide */
+#define SMB_RV_ISA_N (1 << 13) /* User-level interrupts */
+#define SMB_RV_ISA_O (1 << 14) /* Reserved */
+#define SMB_RV_ISA_P (1 << 15) /* Reserved */
+#define SMB_RV_ISA_Q (1 << 16) /* Quad-precision floating-poit */
+#define SMB_RV_ISA_R (1 << 17) /* Reserved */
+#define SMB_RV_ISA_S (1 << 18) /* Supervisor mode */
+#define SMB_RV_ISA_T (1 << 19) /* Reserved */
+#define SMB_RV_ISA_U (1 << 20) /* User mode */
+#define SMB_RV_ISA_V (1 << 21) /* Reserved */
+#define SMB_RV_ISA_W (1 << 22) /* Reserved */
+#define SMB_RV_ISA_X (1 << 23) /* Non-standard extensions */
+#define SMB_RV_ISA_Y (1 << 24) /* Reserved */
+#define SMB_RV_ISA_Z (1 << 25) /* Reserved */
+/* END CSTYLED */
/*
* SMBIOS OEM-specific (Type 132) Processor Extended Information.
@@ -1678,7 +1808,8 @@ typedef struct smbios_memdevice_ext {
#define SMB_VERSION_30 0x0300 /* SMBIOS encoding for DMTF spec 3.0 */
#define SMB_VERSION_31 0x0301 /* SMBIOS encoding for DMTF spec 3.1 */
#define SMB_VERSION_32 0x0302 /* SMBIOS encoding for DMTF spec 3.2 */
-#define SMB_VERSION SMB_VERSION_32 /* SMBIOS latest version definitions */
+#define SMB_VERSION_33 0x0303 /* SMBIOS encoding for DMTF spec 3.3 */
+#define SMB_VERSION SMB_VERSION_33 /* SMBIOS latest version definitions */
#define SMB_O_NOCKSUM 0x1 /* do not verify header checksums */
#define SMB_O_NOVERS 0x2 /* do not verify header versions */
@@ -1735,6 +1866,7 @@ extern int smbios_info_processor(smbios_hdl_t *, id_t, smbios_processor_t *);
extern int smbios_info_extprocessor(smbios_hdl_t *, id_t,
smbios_processor_ext_t *);
extern int smbios_info_cache(smbios_hdl_t *, id_t, smbios_cache_t *);
+extern int smbios_info_pointdev(smbios_hdl_t *, id_t, smbios_pointdev_t *);
extern int smbios_info_port(smbios_hdl_t *, id_t, smbios_port_t *);
extern int smbios_info_extport(smbios_hdl_t *, id_t, smbios_port_ext_t *);
extern int smbios_info_slot(smbios_hdl_t *, id_t, smbios_slot_t *);
@@ -1764,6 +1896,10 @@ extern id_t smbios_info_boot(smbios_hdl_t *, smbios_boot_t *);
extern id_t smbios_info_ipmi(smbios_hdl_t *, smbios_ipmi_t *);
extern int smbios_info_powersup(smbios_hdl_t *, id_t, smbios_powersup_t *);
extern int smbios_info_pciexrc(smbios_hdl_t *, id_t, smbios_pciexrc_t *);
+extern int smbios_info_processor_info(smbios_hdl_t *, id_t,
+ smbios_processor_info_t *);
+extern int smbios_info_processor_riscv(smbios_hdl_t *, id_t,
+ smbios_processor_info_riscv_t *);
extern const char *smbios_psn(smbios_hdl_t *);
@@ -1847,6 +1983,9 @@ extern const char *smbios_memdevice_op_capab_desc(uint_t);
extern const char *smbios_onboard_type_desc(uint_t);
+extern const char *smbios_pointdev_iface_desc(uint_t);
+extern const char *smbios_pointdev_type_desc(uint_t);
+
extern const char *smbios_port_conn_desc(uint_t);
extern const char *smbios_port_type_desc(uint_t);
@@ -1857,6 +1996,13 @@ extern const char *smbios_processor_upgrade_desc(uint_t);
extern const char *smbios_processor_core_flag_name(uint_t);
extern const char *smbios_processor_core_flag_desc(uint_t);
+extern const char *smbios_processor_info_type_desc(uint_t);
+extern const char *smbios_riscv_isa_desc(uint64_t);
+extern const char *smbios_riscv_isa_name(uint64_t);
+extern const char *smbios_riscv_priv_desc(uint_t);
+extern const char *smbios_riscv_priv_name(uint_t);
+extern const char *smbios_riscv_width_desc(uint_t);
+
extern const char *smbios_slot_type_desc(uint_t);
extern const char *smbios_slot_width_desc(uint_t);
extern const char *smbios_slot_usage_desc(uint_t);
diff --git a/usr/src/uts/common/sys/smbios_impl.h b/usr/src/uts/common/sys/smbios_impl.h
index df61892a82..69ca79e94f 100644
--- a/usr/src/uts/common/sys/smbios_impl.h
+++ b/usr/src/uts/common/sys/smbios_impl.h
@@ -366,6 +366,9 @@ typedef struct smb_memdevice {
uint64_t smbmdev_volsize; /* volatile memory size */
uint64_t smbmdev_cachesize; /* cache size */
uint64_t smbmdev_logicalsize; /* logical size */
+ /* Added in SMBIOS 3.3 */
+ uint32_t smbmdev_extspeed; /* Extended device speed */
+ uint32_t smbmdev_extclkspeed; /* Extended clock speed */
} smb_memdevice_t;
#define SMB_MDS_KBYTES 0x8000 /* size in specified in kilobytes */
@@ -386,6 +389,13 @@ typedef struct smb_memdevmap {
uint64_t smbdmap_extend; /* extended ending address */
} smb_memdevmap_t;
+typedef struct smb_pointdev {
+ smb_header_t smbpdev_hdr; /* structure header */
+ uint8_t smbpdev_type; /* device type */
+ uint8_t smbpdev_iface; /* device interface */
+ uint8_t smbpdev_nbuttons; /* number of buttons */
+} smb_pointdev_t;
+
/*
* SMBIOS implementation structure for SMB_TYPE_BATTERY.
*/
@@ -592,6 +602,36 @@ typedef struct smb_obdev_ext {
} smb_obdev_ext_t;
/*
+ * SMBIOS implementation structure for SMB_TYPE_PROCESSOR_INFO
+ */
+typedef struct smb_processor_info {
+ smb_header_t smbpai_hdr; /* structure handle */
+ uint16_t smbpai_proc; /* processor handle */
+ uint8_t smbpai_len; /* length of processor specific data */
+ uint8_t smbpai_type; /* processor type */
+ uint8_t smbpai_data[]; /* processor-specific block */
+} smb_processor_info_t;
+
+typedef struct smb_processor_info_riscv {
+ uint16_t smbpairv_vers; /* structure revision */
+ uint8_t smbpairv_len; /* length of structure */
+ uint8_t smbpairv_hartid[16]; /* id of the hart */
+ uint8_t smbpairv_boot; /* boot hart */
+ uint8_t smbpairv_vendid[16]; /* machine vendor id */
+ uint8_t smbpairv_archid[16]; /* arch vendor id */
+ uint8_t smbpairv_machid[16]; /* machine impl id */
+ uint32_t smbpairv_isa; /* supported ISA */
+ uint8_t smbpairv_privlvl; /* supported privilege levels */
+ uint8_t smbpairv_metdi[16]; /* Machine exception trap delegation */
+ uint8_t smbpairv_mitdi[16]; /* Machine interrupt trap delegation */
+ uint8_t smbpairv_xlen; /* Register width */
+ uint8_t smbpairv_mxlen; /* Machine register width */
+ uint8_t smbpairv_rsvd; /* Reserved */
+ uint8_t smbpairv_sxlen; /* Supervisor register width */
+ uint8_t smbpairv_uxlen; /* User register width */
+} smb_processor_info_riscv_t;
+
+/*
* SMBIOS implementation structure for SUN_OEM_EXT_PROCESSOR.
*/
typedef struct smb_processor_ext {
diff --git a/usr/src/uts/common/sys/sunddi.h b/usr/src/uts/common/sys/sunddi.h
index 8ce8508114..4d943b8e89 100644
--- a/usr/src/uts/common/sys/sunddi.h
+++ b/usr/src/uts/common/sys/sunddi.h
@@ -247,6 +247,8 @@ extern "C" {
/* Fabric Devices */
#define DDI_NT_IB_ATTACHMENT_POINT "ddi_ctl:attachment_point:ib"
/* IB devices */
+#define DDI_NT_CCID_ATTACHMENT_POINT "ddi_ctl:attachment_point:ccid"
+ /* CCID devices */
#define DDI_NT_AV_ASYNC "ddi_av:async" /* asynchronous AV device */
#define DDI_NT_AV_ISOCH "ddi_av:isoch" /* isochronous AV device */
diff --git a/usr/src/uts/common/sys/usb/clients/ccid/ccid.h b/usr/src/uts/common/sys/usb/clients/ccid/ccid.h
new file mode 100644
index 0000000000..0437a957ba
--- /dev/null
+++ b/usr/src/uts/common/sys/usb/clients/ccid/ccid.h
@@ -0,0 +1,311 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+#ifndef _SYS_USB_CCID_H
+#define _SYS_USB_CCID_H
+
+/*
+ * CCID class driver definitions.
+ */
+
+#include <sys/stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Values for various Hardware, Mechanical, and Pin features. These come from
+ * the device's class descriptor.
+ */
+typedef enum ccid_class_voltage {
+ CCID_CLASS_VOLT_AUTO = 0x00,
+ CCID_CLASS_VOLT_5_0 = 0x01,
+ CCID_CLASS_VOLT_3_0 = 0x02,
+ CCID_CLASS_VOLT_1_8 = 0x04
+} ccid_class_voltage_t;
+
+typedef enum ccid_class_mechanical {
+ CCID_CLASS_MECH_CARD_ACCEPT = 0x01,
+ CCID_CLASS_MECH_CARD_EJECT = 0x02,
+ CCID_CLASS_MECH_CARD_CAPTURE = 0x04,
+ CCID_CLASS_MECH_CARD_LOCK = 0x08
+} ccid_class_mechanical_t;
+
+typedef enum ccid_class_features {
+ CCID_CLASS_F_AUTO_PARAM_ATR = 0x00000002,
+ CCID_CLASS_F_AUTO_ICC_ACTIVATE = 0x00000004,
+ CCID_CLASS_F_AUTO_ICC_VOLTAGE = 0x00000008,
+ CCID_CLASS_F_AUTO_ICC_CLOCK = 0x00000010,
+ CCID_CLASS_F_AUTO_BAUD = 0x00000020,
+ CCID_CLASS_F_AUTO_PARAM_NEG = 0x00000040,
+ CCID_CLASS_F_AUTO_PPS = 0x00000080,
+ CCID_CLASS_F_ICC_CLOCK_STOP = 0x00000100,
+ CCID_CLASS_F_ALTNAD_SUP = 0x00000200,
+ CCID_CLASS_F_AUTO_IFSD = 0x00000400,
+ CCID_CLASS_F_TPDU_XCHG = 0x00010000,
+ CCID_CLASS_F_SHORT_APDU_XCHG = 0x00020000,
+ CCID_CLASS_F_EXT_APDU_XCHG = 0x00040000,
+ CCID_CLASS_F_WAKE_UP = 0x00100000
+} ccid_class_features_t;
+
+typedef enum ccid_class_pin {
+ CCID_CLASS_PIN_VERIFICATION = 0x01,
+ CCID_CLASS_PIN_MODIFICATION = 0x02
+} ccid_class_pin_t;
+
+/*
+ * CCID Class Descriptor
+ *
+ * This structure represents the CCID class descriptor. Note, it should not be a
+ * packed structure. This is designed to be a native representation. The raw
+ * structure will be parsed into this instead.
+ */
+typedef struct ccid_class_descr {
+ uint8_t ccd_bLength;
+ uint8_t ccd_bDescriptorType;
+ uint16_t ccd_bcdCCID;
+ uint8_t ccd_bMaxSlotIndex;
+ uint8_t ccd_bVoltageSupport;
+ uint32_t ccd_dwProtocols;
+ uint32_t ccd_dwDefaultClock;
+ uint32_t ccd_dwMaximumClock;
+ uint8_t ccd_bNumClockSupported;
+ uint32_t ccd_dwDataRate;
+ uint32_t ccd_dwMaxDataRate;
+ uint8_t ccd_bNumDataRatesSupported;
+ uint32_t ccd_dwMaxIFSD;
+ uint32_t ccd_dwSyncProtocols;
+ uint32_t ccd_dwMechanical;
+ uint32_t ccd_dwFeatures;
+ uint32_t ccd_dwMaxCCIDMessageLength;
+ uint8_t ccd_bClassGetResponse;
+ uint8_t ccd_bClassEnvelope;
+ uint16_t ccd_wLcdLayout;
+ uint8_t ccd_bPinSupport;
+ uint8_t ccd_bMaxCCIDBusySlots;
+} ccid_class_descr_t;
+
+/*
+ * Definitions for the supported versions of the CCID specification. The version
+ * is encoded in binary encoded decimal. The major version is in the upper 8
+ * bits and the minor version is in the lower 8 bits. We currently check for the
+ * major version to match.
+ */
+#define CCID_VERSION_MAJOR(ver) (((ver) & 0xff00) >> 8)
+#define CCID_VERSION_MINOR(ver) ((ver) & 0x00ff)
+#define CCID_VERSION_ONE 0x01
+
+/*
+ * This structure is used as the data for the CCID_REQUEST_SET_PARAMS request
+ * and the CCID_RESPONSE_PARAMETERS response. There are different structures for
+ * T=0 and T=1. These come from CCID r1.1 / Section 6.1.7.
+ */
+typedef struct ccid_params_t0 {
+ uint8_t cp0_bmFindexDindex;
+ uint8_t cp0_bmTCCKST0;
+ uint8_t cp0_bGuardTimeT0;
+ uint8_t cp0_bWaitingIntegerT0;
+ uint8_t cp0_bClockStop;
+} __packed ccid_params_t0_t;
+
+#define CCID_P_TCCKST0_DIRECT 0x00
+#define CCID_P_TCCKST0_INVERSE 0x02
+
+typedef struct ccid_params_t1 {
+ uint8_t cp1_bmFindexDindex;
+ uint8_t cp1_bmTCCKST1;
+ uint8_t cp1_bGuardTimeT1;
+ uint8_t cp1_bmWaitingIntegersT1;
+ uint8_t cp1_bClockStop;
+ uint8_t cp1_bIFSC;
+ uint8_t cp1_bNadValue;
+} __packed ccid_params_t1_t;
+
+typedef union ccid_params {
+ ccid_params_t0_t ccp_t0;
+ ccid_params_t1_t ccp_t1;
+} ccid_params_t;
+
+#define CCID_P_FI_DI(fi, di) ((((fi) & 0x0f) << 4) | ((di) & 0x0f))
+
+/*
+ * Everything below this point is reserved for the kernel.
+ */
+#ifdef _KERNEL
+
+/*
+ * These values come from CCID r1.1.0 Table 5.1-1 'Smart Card Device
+ * Descriptors'
+ */
+#define CCID_DESCR_TYPE 0x21
+#define CCID_DESCR_LENGTH 0x36
+
+
+/*
+ * Minimum and maximum value for a sequence number in the CCID specification.
+ * The sequence is a 1 byte unsigned value. The values are inclusive. We reserve
+ * the value of 0x00 so that we can use it as a sentinel in the ccid_command_t
+ * structure to know when we should or shouldn't free a command structure's
+ * sequence number back to the id space.
+ */
+#define CCID_SEQ_MIN 0x01
+#define CCID_SEQ_MAX UINT8_MAX
+
+
+/*
+ * All structures from the specification must be packed.
+ */
+
+/*
+ * Interrupt-IN messages codes.
+ */
+typedef enum ccid_intr_code {
+ CCID_INTR_CODE_SLOT_CHANGE = 0x50,
+ CCID_INTR_CODE_HW_ERROR = 0x51
+} __packed ccid_intr_code_t;
+
+typedef enum ccid_intr_hwerr_code {
+ CCID_INTR_HWERR_OVERCURRENT = 0x01
+} __packed ccid_intr_hwerr_code_t;
+
+typedef struct ccid_intr_slot {
+ uint8_t cis_type;
+ uint8_t cis_state[];
+} __packed ccid_intr_slot_t;
+
+typedef struct ccid_intr_hwerr {
+ uint8_t cih_type;
+ uint8_t cih_slot;
+ uint8_t cih_seq;
+ uint8_t cih_code;
+} __packed ccid_intr_hwerr_t;
+
+/*
+ * Message request codes. These codes are based on CCID r1.1.0 Table 6.1-1
+ * 'Summary of Bulk-Out Messages'. The name from the standard is to the right of
+ * the enum.
+ */
+typedef enum ccid_request_code {
+ CCID_REQUEST_POWER_ON = 0x62, /* PC_to_RDR_IccPowerOn */
+ CCID_REQUEST_POWER_OFF = 0x63, /* PC_to_RDR_IccPowerOff */
+ CCID_REQUEST_SLOT_STATUS = 0x65, /* PC_to_RDR_GetSlotStatus */
+ CCID_REQUEST_TRANSFER_BLOCK = 0x6f, /* PC_to_RDR_XfrBlock */
+ CCID_REQUEST_GET_PARAMS = 0x6c, /* PC_to_RDR_GetParameters */
+ CCID_REQUEST_RESET_PARAMS = 0x6d, /* PC_to_RDR_ResetParameters */
+ CCID_REQUEST_SET_PARAMS = 0x61, /* PC_to_RDR_SetParameters */
+ CCID_REQUEST_ESCAPE = 0x6b, /* PC_to_RDR_Escape */
+ CCID_REQUEST_ICC_CLOCK = 0x6e, /* PC_to_RDR_IccClock */
+ CCID_REQUEST_T0APDU = 0x6a, /* PC_to_RDR_T0APDU */
+ CCID_REQUEST_SECURE = 0x69, /* PC_to_RDR_Secure */
+ CCID_REQUEST_MECHANICAL = 0x71, /* PC_to_RDR_Mechanica */
+ CCID_REQEUST_ABORT = 0x72, /* PC_to_RDR_Abort */
+ CCID_REQUEST_DATA_CLOCK = 0x73 /* PC_to_RDR_SetDataRateAnd */
+ /* ClockFrequency */
+} __packed ccid_request_code_t;
+
+/*
+ * Message request codes. These codes are based on CCID r1.1.0 Table 6.2-1
+ * 'Summary of Bulk-In Messages'. The name from the standard is to the right of
+ * the enum.
+ */
+typedef enum ccid_response_code {
+ CCID_RESPONSE_DATA_BLOCK = 0x80, /* RDR_to_PC_DataBlock */
+ CCID_RESPONSE_SLOT_STATUS = 0x81, /* RDR_to_PC_SlotStatus */
+ CCID_RESPONSE_PARAMETERS = 0x82, /* RDR_to_PC_Parameters */
+ CCID_RESPONSE_ESCAPE = 0x83, /* RDR_to_PC_Escape */
+ CCID_RESPONSE_DATA_CLOCK = 0x84 /* RDR_to_PC_DataRateAnd */
+ /* ClockFrequency */
+} __packed ccid_response_code_t;
+
+/*
+ * This represents the CCID command header that is used for every request and
+ * response.
+ */
+typedef struct ccid_header {
+ uint8_t ch_mtype;
+ uint32_t ch_length; /* Length of ch_data in bytes */
+ uint8_t ch_slot; /* CCID slot to target */
+ uint8_t ch_seq; /* Request/Response sequence num */
+ uint8_t ch_param0; /* Request/Response specific */
+ uint8_t ch_param1; /* Request/Response specific */
+ uint8_t ch_param2; /* Request/Response specific */
+ uint8_t ch_data[]; /* Optional Request/Response Data */
+} __packed ccid_header_t;
+
+/*
+ * This structure is used as the data for the CCID_REQUEST_DATA_CLOCK and
+ * CCID_RESPONSE_DATA_CLOCK commands.
+ */
+typedef struct ccid_data_clock {
+ uint32_t cdc_clock;
+ uint32_t cdc_data;
+} __packed ccid_data_clock_t;
+
+/*
+ * Macros and constants to take apart the slot status (in ch_param1) when a CCID
+ * reply comes in.
+ */
+#define CCID_REPLY_ICC(x) (x & 0x3)
+#define CCID_REPLY_STATUS(x) ((x & 0xc0) >> 6)
+
+typedef enum {
+ CCID_REPLY_ICC_ACTIVE = 0,
+ CCID_REPLY_ICC_INACTIVE,
+ CCID_REPLY_ICC_MISSING
+} __packed ccid_reply_icc_status_t;
+
+typedef enum {
+ CCID_REPLY_STATUS_COMPLETE = 0,
+ CCID_REPLY_STATUS_FAILED,
+ CCID_REPLY_STATUS_MORE_TIME
+} __packed ccid_reply_command_status_t;
+
+/*
+ * Errors that are defined based when commands fail. These are based on CCID
+ * r.1.1.0 Table 6.2-2 'Slot error register when bmCommandStatus = 1'.
+ */
+typedef enum ccid_command_err {
+ CCID_ERR_CMD_ABORTED = 0xff,
+ CCID_ERR_ICC_MUTE = 0xfe,
+ CCID_ERR_XFR_PARITY_ERROR = 0xfd,
+ CCID_ERR_XFR_OVERRUN = 0xfc,
+ CCID_ERR_HW_ERROR = 0xfb,
+ CCID_ERR_BAD_ATR_TS = 0xf8,
+ CCID_ERR_BAD_ATR_TCK = 0xf7,
+ CCID_ERR_ICC_PROTOCOL_NOT_SUPPORTED = 0xf6,
+ CCID_ERR_ICC_CLASS_NOT_SUPPORTED = 0xf5,
+ CCID_ERR_PROCEDURE_BYTE_CONFLICT = 0xf4,
+ CCID_ERR_DEACTIVATED_PROTOCOL = 0xf3,
+ CCID_ERR_BUSY_WITH_AUTO_SEQUENCE = 0xf2,
+ CCID_ERR_PIN_TIMEOUT = 0xf0,
+ CCID_ERR_PIN_CANCELLED = 0xef,
+ CCID_ERR_CMD_SLOT_BUSY = 0xe0,
+ CCID_ERR_CMD_NOT_SUPPORTED = 0x00
+} __packed ccid_command_err_t;
+
+/*
+ * Maximum size of an APDU (application data unit) payload. There are both short
+ * and extended ADPUs. At this time, we only support the short ADPUs.
+ */
+#define CCID_APDU_LEN_MAX 261
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_USB_CCID_H */
diff --git a/usr/src/uts/common/sys/usb/clients/ccid/uccid.h b/usr/src/uts/common/sys/usb/clients/ccid/uccid.h
new file mode 100644
index 0000000000..9aee0559b6
--- /dev/null
+++ b/usr/src/uts/common/sys/usb/clients/ccid/uccid.h
@@ -0,0 +1,133 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+#ifndef _SYS_USB_UCCID_H
+#define _SYS_USB_UCCID_H
+
+/*
+ * Definitions for the userland CCID interface.
+ */
+
+#include <sys/types.h>
+#include <sys/usb/clients/ccid/ccid.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The maximum size of a normal APDU. This is the upper bound of what a user can
+ * read or write to a given card.
+ */
+#define UCCID_APDU_SIZE_MAX 261
+
+/*
+ * This is the maximum length of an ATR as per ISO/IEC 7816-3:2006.
+ */
+#define UCCID_ATR_MAX 33
+
+
+#define UCCID_IOCTL (('u' << 24) | ('c' << 16) | ('d') << 8)
+
+#define UCCID_VERSION_ONE 1
+#define UCCID_CURRENT_VERSION UCCID_VERSION_ONE
+
+/*
+ * Attempt to obtain exclusive access. If the UCN_TXN_DONT_BLOCK flag is
+ * specified, the ioctl will return immediately if exclusive access cannot be
+ * gained. Otherwise, it will block in an interruptible fashion. The argument is
+ * a uccid_cmd_txn_begin_t.
+ */
+#define UCCID_CMD_TXN_BEGIN (UCCID_IOCTL | 0x01)
+#define UCCID_TXN_DONT_BLOCK 0x01
+
+typedef struct uccid_cmd_txn_begin {
+ uint32_t uct_version;
+ uint32_t uct_flags;
+} __packed uccid_cmd_txn_begin_t;
+
+/*
+ * Relinquish exclusive access. Takes a uccid_cmd_txn_end_t. The callers should
+ * specify one of UCCID_TXN_END_RESET or UCCID_TXN_END_RELEASE. These indicate
+ * what behavior should be taken when we release the transaction. It is
+ * considered an error if neither is specified. If the caller exits without
+ * calling this function, then the ICC will be reset.
+ */
+#define UCCID_CMD_TXN_END (UCCID_IOCTL | 0x02)
+#define UCCID_TXN_END_RESET 0x01
+#define UCCID_TXN_END_RELEASE 0x02
+
+typedef struct uccid_cmd_txn_end {
+ uint32_t uct_version;
+ uint32_t uct_flags;
+} __packed uccid_cmd_txn_end_t;
+
+/*
+ * Obtain the status of the slot. Returns a filled-in uccid_cmd_status_t.
+ */
+#define UCCID_CMD_STATUS (UCCID_IOCTL | 0x3)
+
+/*
+ * Protocol definitions. This should match common/ccid/atr.h.
+ */
+typedef enum {
+ UCCID_PROT_T0 = 1 << 0,
+ UCCID_PROT_T1 = 1 << 1
+} uccid_prot_t;
+
+/*
+ * Bits for UCS Status
+ */
+#define UCCID_STATUS_F_CARD_PRESENT 0x01
+#define UCCID_STATUS_F_CARD_ACTIVE 0x02
+#define UCCID_STATUS_F_PRODUCT_VALID 0x04
+#define UCCID_STATUS_F_SERIAL_VALID 0x08
+#define UCCID_STATUS_F_PARAMS_VALID 0x10
+
+typedef struct uccid_cmd_status {
+ uint32_t ucs_version;
+ uint32_t ucs_status;
+ int32_t ucs_instance;
+ uint32_t ucs_slot;
+ uint8_t ucs_atr[UCCID_ATR_MAX];
+ uint8_t ucs_atrlen;
+ uint8_t ucs_pad[6];
+ int8_t ucs_product[256];
+ int8_t ucs_serial[256];
+ ccid_class_descr_t ucs_class;
+ uccid_prot_t ucs_prot;
+ ccid_params_t ucs_params;
+} __packed uccid_cmd_status_t;
+
+/*
+ * Modify the state of the ICC, if present.
+ */
+#define UCCID_CMD_ICC_MODIFY (UCCID_IOCTL | 0x04)
+
+#define UCCID_ICC_POWER_ON 0x01
+#define UCCID_ICC_POWER_OFF 0x02
+#define UCCID_ICC_WARM_RESET 0x03
+
+typedef struct uccid_cmd_icc_modify {
+ uint32_t uci_version;
+ uint32_t uci_action;
+} __packed uccid_cmd_icc_modify_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _SYS_USB_UCCID_H */
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel
index b24530aa36..f7e22ea88e 100644
--- a/usr/src/uts/intel/Makefile.intel
+++ b/usr/src/uts/intel/Makefile.intel
@@ -514,6 +514,7 @@ DRV_KMODS += ses
#
# USB specific modules
#
+DRV_KMODS += ccid
DRV_KMODS += hid
DRV_KMODS += hubd
DRV_KMODS += uhci
diff --git a/usr/src/uts/intel/ccid/Makefile b/usr/src/uts/intel/ccid/Makefile
new file mode 100644
index 0000000000..a7c8b96d70
--- /dev/null
+++ b/usr/src/uts/intel/ccid/Makefile
@@ -0,0 +1,42 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+MODULE = ccid
+OBJECTS = $(CCID_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+CPPFLAGS += -I$(SRC)/common/ccid
+
+LDFLAGS += -dy -N misc/usba
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases
index 6ce92d0bb5..cc82243020 100644
--- a/usr/src/uts/intel/os/driver_aliases
+++ b/usr/src/uts/intel/os/driver_aliases
@@ -216,6 +216,7 @@ bnxe "pciex14e4,16a5"
bnxe "pciex14e4,16ab"
bnxe "pciex14e4,16ae"
bscbus "SVI0101"
+ccid "usbif,classb"
ce "pci100b,35"
ce "pci108e,abba"
chxge "pci1425,7"
diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major
index 17355a771a..a50496f1fa 100644
--- a/usr/src/uts/intel/os/name_to_major
+++ b/usr/src/uts/intel/os/name_to_major
@@ -308,3 +308,4 @@ ufm 311
ufmtest 312
imcstub 313
imc 314
+ccid 315